返回对象中项目总数的最佳RESTful方法是什么?

Mar*_*ean 131 rest restful-url

我正在为我参与的大型社交网站开发REST API服务.到目前为止,它的工作效果很好.我可以发出GET,POST,PUTDELETE请求对象的URL和影响我的数据.但是,此数据被分页(一次限制为30个结果).

但是,通过我的API获取发言人总数的最佳REST方式是什么?

目前,我向URL结构发出请求,如下所示:

  • / api / members-返回一个成员列表(如上所述,每次30个成员)
  • / api/members/1 -影响单个成员,具体取决于使用的请求方法

我的问题是:如何使用类似的URL结构来获取应用程序中的成员总数?显然id只需要返回30个结果就可以只请求该字段(类似于Facebook的图谱API)并计算结果是无效的.

Fra*_*nov 79

虽然对/ API/users的响应被分页并且只返回30条记录,但是没有什么能阻止您在响应中包括记录总数和其他相关信息,如页面大小,页码/偏移量等.

StackOverflow API是同一设计的一个很好的例子.以下是Users方法的文档 - https://api.stackexchange.com/docs/users

  • @Donal"next"rel已在IANA注册http://www.iana.org/assignments/link-relations/link-relations.txt (4认同)
  • 返回不是项列表的对象不是REST API的正确实现,但REST不提供任何方法来获取部分结果列表.所以为了尊重这一点,我认为我们应该使用标头来传输其他信息,如总计,下一页标记和上一页标记.我从未尝试过,我需要其他开发人员的建议. (4认同)
  • +1:如果要获取获取限制,肯定是最RESTful的事情. (3认同)
  • @Darrel - 是的,可以使用有效负载中的任何类型的“下一个”标志来完成。我只是觉得在响应中包含集合项的总数本身很有价值,并且可以作为“下一个”标志。 (3认同)
  • @bzim你会知道有一个下一页要获取,因为有一个链接rel ="next". (2认同)

Ond*_*zek 69

我更喜欢使用HTTP标头来获取这种上下文信息.

对于元素总数,我使用X-total-count标题.
有关下一页,上一页等的链接,我使用http Link标题:http :
//www.w3.org/wiki/LinkHeader

Github也这样做:https://developer.github.com/v3/#pagination

在我看来它更干净,因为当你返回不支持超链接的内容(即二进制文件,图片)时它也可以使用.

  • [RFC6648](https://tools.ietf.org/html/rfc6648)不赞成使用非标准参数名称加字符串“ X-”作为前缀的约定。 (3认同)
  • 我的建议是首先避免发明自定义标头。使用现有的东西。如果人们提出了“X-Total-Count”并且客户端为其提供了内置支持,那么您“尊重 RFC6648”并使用“Resource-Count”对任何人都没有帮助。举个例子:Google 搜索“标题剩余总计数”在该页面上 10 个搜索结果中的 6 个的**标题**中命名为“X-Total-Count”,但没有提及此处建议的替代方案。尽管很蹩脚,但“X-Total-Count”比本页提到的任何其他解决方案更像是一个标准。只需使用它即可。 (2认同)

Sti*_*itt 61

我最近一直在对这个和其他REST分页相关的问题进行一些广泛的研究,并认为在这里添加我的一些发现是有建设性的.我正在扩大这个问题,包括对分页和计数的想法,因为它们是无关紧要的.

寻呼元数据以响应头的形式包含在响应中.这种方法的最大好处是响应有效负载本身就是请求者所要求的实际数据.使对寻呼信息不感兴趣的客户端的响应更容易.

野外使用了大量(标准和自定义)标头来返回与寻呼相关的信息,包括总计数.

X-合计数

X-Total-Count: 234
Run Code Online (Sandbox Code Playgroud)

这在我在野外发现的一些 API中使用.还有NPM包用于添加对此标头的支持,例如Loopback.有些文章也建议设置此标题.

它通常与Link标题结合使用,这是一种非常好的分页解决方案,但缺少总计数信息.

链接

Link: </TheBook/chapter2>;
      rel="previous"; title*=UTF-8'de'letztes%20Kapitel,
      </TheBook/chapter4>;
      rel="next"; title*=UTF-8'de'n%c3%a4chstes%20Kapitel
Run Code Online (Sandbox Code Playgroud)

我觉得,从这个题目读了很多,那一般的共识是使用Link使用提供分页链接到客户rel=next,rel=previous等这里的问题是,它缺乏一共有多少记录有信息,这是为什么许多API将其与X-Total-Count标题相结合.

或者,某些API(例如JsonApi标准)使用该Link格式,但将信息添加到响应信封中而不是标头中.这简化了对元数据的访问(并创建了添加总计数信息的位置),代价是增加了访问实际数据本身的复杂性(通过添加包络).

内容范围

Content-Range: items 0-49/234
Run Code Online (Sandbox Code Playgroud)

由一篇名为Range header的博客文章推荐,我选择你(用于分页)!.作者强烈建议使用RangeContent-Range标题进行分页.当我们仔细阅读这些标头上 RFC时,我们发现RFC实际上已经预期将其意义扩展到字节范围之外并且是明确允许的.在items代替的上下文中使用时bytes,Range标头实际上为我们提供了一种方法,既可以请求特定范围的项目,也可以指示响应项目与总结果的范围.此标题还提供了显示总计数的好方法.它是一个真正的标准,主要是一对一地映射到分页.它也用于野外.

信封

许多API,包括我们最喜欢的Q&A网站中的 API,都使用信封,这是用于添加有关数据的元信息的数据的包装器.此外,ODataJsonApi标准都使用响应包络.

这个(imho)的一大缺点是处理响应数据变得更加复杂,因为必须在信封的某处找到实际数据.此信封还有许多不同的格式,您必须使用正确的格式.这说明来自OData和JsonApi的响应包络是非常不同的,OData在响应中的多个点处混合了元数据.

单独的端点

我认为在其他答案中已经涵盖了足够的内容.我没有对此进行过多的调查,因为我同意这些令人困惑的评论,因为您现在有多种类型的端点.我认为如果每个端点都代表一个(集合)资源,这是最好的.

进一步的想法

我们不仅要传达与响应相关的分页元信息,还要允许客户端请求特定的页面/范围.有趣的是,还要看一下这方面的最终结果是一致的解决方案.在这里我们也可以使用标题(Range标题似乎非常合适),或其他机制,如查询参数.有些人主张处理结果作为单独的资源,这可能是有意义的一些使用情况下的页面(例如/books/231/pages/52,我最终选择经常使用的请求参数,如野生范围pagesize,page[size]并且limit等除了支持的Range标题(和请求参数以及).

  • 我认为可以在[RFC第14.5节](https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.5)中找到最明确的证据:`acceptable-ranges = 1#range单位| 尽管规范本身仅定义了“字节”,但我认为这种表达方式显然为“字节”以外的其他范围单位留出了空间。 (2认同)

bzl*_*zlm 23

您可以将计数作为自定义HTTP标头返回以响应HEAD请求.这样,如果客户端只想要计数,则不需要返回实际列表,也不需要额外的URL.

(或者,如果您处于从端点到端点的受控环境中,则可以使用自定义HTTP谓词,例如COUNT.)

  • @Donal我知道.但所有好的答案都已经采取了.:( (21认同)
  • "自定义HTTP标头"?这将是一个令人惊讶的标题,这反过来我认为RESTful API应该是.最终,它应该不足为奇. (4认同)
  • 在受控环境中,这可能不足为奇,因为它可能会在内部使用并基于开发人员的API策略.我认为这在某些情况下是一个很好的解决方案,值得在这里作为一个可能不寻常的解决方案的说明. (4认同)

Rob*_*nik 22

当您不需要实际物品时的替代方案

Franci Penov的答案当然是最好的方法,因此您总是会返回项目以及有关您所请求实体的所有其他元数据.这就是它应该做的方式.

但有时返回所有数据没有意义,因为你根本不需要它们.也许您需要的是有关您请求的资源的元数据.像总计数或页数或其他东西.在这种情况下,您始终可以使用URL查询告诉您的服务不要返回项目,而只是返回元数据,如:

/api/members?metaonly=true
/api/members?includeitems=0
Run Code Online (Sandbox Code Playgroud)

或类似的东西......

  • 在头文件中嵌入此信息的优点是您可以发出HEAD请求以获取计数. (9认同)
  • @EralpB 感谢您重新发明轮子并弄乱了 API!?HEAD 是在 HTTP 中指定的。`metaonly` 或 `includeitems` 不是。 (3认同)
  • @felixfbecker 确实如此,感谢您重新发明轮子并用各种不同的机制使 API 变得混乱:) (2认同)
  • @felixfbecker 仅“完全”是为您准备的,其余的则是为 OP 准备的。对困惑感到抱歉。 (2认同)

adn*_*ili 11

我建议为它添加标题,例如:

HTTP/1.1 200

Pagination-Count: 100
Pagination-Page: 5
Pagination-Limit: 20
Content-Type: application/json

[
  {
    "id": 10,
    "name": "shirt",
    "color": "red",
    "price": "$23"
  },
  {
    "id": 11,
    "name": "shirt",
    "color": "blue",
    "price": "$25"
  }
]
Run Code Online (Sandbox Code Playgroud)

有关详情,请参阅:

https://github.com/adnan-kamili/rest-api-response-format

对于招摇文件:

https://github.com/adnan-kamili/swagger-response-template


Ste*_*ods 5

一个新的端点 > /api/members/count 怎么样,它只调用 Members.Count() 并返回结果

  • 为计数提供显式端点使其成为独立的可寻址资源。它会起作用,但会给任何刚接触 API 的人提出有趣的问题 - 集合成员的计数是否是集合中的单独资源?我可以使用 PUT 请求更新它吗?它是针对空集合而存在还是仅当其中有项目时才存在?如果可以通过对“/api”的 POST 请求来创建“members”集合,那么“/api/members/count”也会作为副作用被创建,还是我必须执行显式 POST 请求才能创建它在请求之前?:-) (34认同)

Lep*_*ron 5

从“ X-”开始,不推荐使用前缀。(请参阅:https : //tools.ietf.org/html/rfc6648

我们发现“接受范围”是映射分页范围的最佳选择:https : //tools.ietf.org/html/rfc7233#section-2.3 由于“范围单位”可能是“字节”或“令牌”。两者都不代表自定义数据类型。(请参阅:https : //tools.ietf.org/html/rfc7233#section-4.2)仍然指出

HTTP / 1.1实现可以忽略使用其他单位指定的范围。

这表明:使用自定义范围单位不违反协议,但是可以忽略。

这样,我们就必须将Accept-Ranges设置为“ members”或我们期望的任何范围单位类型。此外,还将“内容范围”设置为当前范围。(请参阅:https//www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.12

无论哪种方式,我都会坚持RFC7233的建议(https://tools.ietf.org/html/rfc7233#page-8)发送206而不是200:

如果所有前提条件都为真,则服务器支持
目标资源的Range 标头字段,并且指定的范围
有效且可满足要求(如第2.1节中所定义),服务器应
发送206(部分内容)响应有效负载包含 第4节中定义的
对应于
请求的可满足范围的一个或多个局部表示。

因此,因此,我们将拥有以下HTTP标头字段:

对于部分内容:

206 Partial Content
Accept-Ranges: members
Content-Range: members 0-20/100
Run Code Online (Sandbox Code Playgroud)

有关完整内容:

200 OK
Accept-Ranges: members
Content-Range: members 0-20/20
Run Code Online (Sandbox Code Playgroud)