设计REST Web服务以在浏览器中消耗二进制数据的最佳方法

35
I希望您能翻译以下内容:我正在开发一个JSON REST网络服务,将被Backbone.js构建的单页面应用程序使用。
该API将允许用户上传与某个实体相关的文件,例如与项目相关的PDF报告。
通过谷歌搜索并在Stack Overflow上进行一些研究,我找到了以下可能的方法:
第一种方法:base64编码数据字段。
POST: /api/projects/234/reports
{
  author: 'xxxx',
  abstract: 'xxxx',
  filename: 'xxxx',
  filesize: 222,
  content: '<base64 encoded binary data>'
}

第二种方法:多部分表单提交:

POST: /api/projects/234/reports
{
  author: 'xxxx',
  abstract: 'xxxx',
}

作为回应,我将收到一个报告ID,然后我将发出另一个POST请求。
POST: /api/projects/234/reports/1/content
enctype=multipart/form-data

然后只需发送二进制数据

(可以参考这个链接: https://dev59.com/C2865IYBdhLWcg3wLrpE#3938816)

第三种方法:将二进制数据发布到单独的资源中并保存href

首先在客户端生成一个随机密钥,然后将二进制内容发布到该位置。

POST: /api/files/E4304205-29B7-48EE-A359-74250E19EFC4
enctype=multipart/form-data

然后

POST: /api/projects/234/reports
{
  author: 'xxxx',
  abstract: 'xxxx',
  filename: 'xxxx',
  filesize: 222,
  href: '/api/files/E4304205-29B7-48EE-A359-74250E19EFC4'
}

我想知道是否有其他方法可用,每种方法的优缺点以及处理此类需求的任何已建立的方式

我认为第一种方法的最大缺点是,我必须在客户端完全加载和base64编码文件

一些有用的资源:

(参见此处: https://dev59.com/b2865IYBdhLWcg3wA5yc#4032079)

3个回答

19

我的研究结果:

  1. 单请求 (包含数据)

    请求包含元数据。数据是元数据的一个属性并被编码(例如:Base64)。

    优点:

    • 事务性
    • 每次有效(不存在丢失的元数据或数据)

    缺点:

    • 编码使请求非常大

    例子:

  2. 单请求 (多部分)

    请求包含一个或多个具有元数据和数据的部分。

    内容类型:

    优点:

    • 事务性
    • 每次有效(不存在丢失的元数据或数据)

    缺点:

    • 内容类型协商很复杂
    • 数据的内容类型在 WADL 中不可见

    例子:

    • Confluence(使用部分来处理数据和元数据)
    • Jira(其中一部分为数据,仅有元数据的部分头用于文件名和MIME类型)
    • Bitbucket(其中一部分为数据,没有元数据)
    • Google Drive(其中一部分为元数据,另一部分为数据)
  3. 单请求(元数据在HTTP标头和URL中)

    请求体包含数据,HTTP标头和URL包含元数据。

    优点:

    • 事务性
    • 始终有效(无元数据或数据缺失)

    缺点:

    • 无法进行嵌套元数据

    示例:

  4. 两次请求

    一次请求用于元数据,一次或多次请求用于数据。

    优点:

    • 可伸缩性(例如:数据请求可以发送到存储库服务器)
    • 可恢复(请参见例如Google Drive

    缺点:

    • 不具有事务性
    • 不始终有效(在第二个请求之前,缺少一个部分)

    示例:


8

我暂时想不到其他方法。

在你提供的三种方法中,我使用过最多的是第三种。我认为最大的区别在于第一种方法和其他两种方法之间:将元数据和内容分离成两个资源。

  • 优点:可伸缩性
    • 虽然你的解决方案涉及发布到同一服务器上,但可以轻松更改为将内容上传指向单独的服务器(例如Amazon S3)
    • 在第一种方法中,为用户提供元数据的同一服务器将被一个大型上传进程阻塞。
  • 缺点:孤立数据 / 增加复杂性
    • 失败的上传(元数据或内容)将在服务器数据库中留下孤立数据
    • 可以通过定期作业清除孤立的数据,但这会增加代码复杂度
    • 第二种方法减少了孤立数据的可能性,代价是需要更长的客户端等待时间,因为您需要在第一个POST的响应上进行阻塞。

第一种方法似乎是编写最简单的方法。但是,如果你预计这项服务很少使用,并且你可以对用户文件上传设置合理的限制,那么我只会选择第一种方法。


5

我认为最终的方法是第三个(分离资源),原因是它允许我最大限度地利用HTTP标准所提供的价值,这与我对REST API的理解相符。例如,假设有一个良好的HTTP客户端在使用,您可以获得以下好处:

  • 内容压缩:通过允许服务器响应已压缩的结果(如果客户端表示支持),您可以进行优化,您的API不会发生变化,现有客户端仍然可以使用,未来的客户端也可以利用此功能。
  • 缓存:If-Modified-Since、ETag等。客户端可以避免重新获取二进制数据。
  • 内容类型抽象化:例如,您需要上传图像,它可以是image/jpegimage/png类型。HTTP头部的AcceptContent-type为我们在客户端和服务器之间协商此事提供了一些优雅的语义,而无需将其全部硬编码为我们的模式和/或API的一部分。

另一方面,如果涉及的二进制数据是必要的,我认为结论就是这种方法并不是最简单的。这时候,Eric Hu的答案中列出的缺点将会发挥作用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接