将元数据添加到RESTful JSON响应的最佳做法是什么?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了将元数据添加到RESTful JSON响应的最佳做法是什么?相关的知识,希望对你有一定的参考价值。
背景
我们正在构建一个Restful API,它应该将数据对象作为JSON返回。在大多数情况下,返回数据对象很好,但在某些情况下,f.ex。分页或验证,我们需要在响应中添加一些元数据。
到目前为止我们有什么
我们已经包装了所有json响应,例如:
{
"metadata" :{
"status": 200|500,
"msg": "Some message here",
"next": "http://api.domain.com/users/10/20"
...
},
"data" :{
"id": 1001,
"name": "Bob"
}
}
优点
- 我们可以为响应添加有用的元数据
缺点
- 在大多数情况下,我们不需要元数据字段,这增加了json格式的复杂性
- 由于它不再是数据对象,而更像是包络响应,因此我们无法在f.ex backbone.js中立即使用响应而无需提取数据对象。
题
将元数据添加到json响应的最佳做法是什么?
UPDATE
到目前为止,我得到的答案如下:
- 删除
metadata.status
并返回http协议中的http响应代码(200,500 ...) - 将错误消息添加到http 500响应的正文
- 对于分页,我自然会有一些元数据告诉分页结构,以及嵌套在该结构中的数据
- 可以将少量元数据添加到http标头(X-something)
您有几种方法可以在RESTful API中传递元数据:
- Http状态代码
- 头
- 响应机构
对于metadata.status,请使用Http状态代码,这就是为什么!如果元数据是指整个响应,则可以将其添加为标题字段。如果元数据仅指代响应的一部分,则必须将元数据作为对象的一部分嵌入.DON'T将整个响应包装在人工信封中,并将包装器拆分为数据和元数据。
最后,通过您的选择在您的API中保持一致。
一个很好的例子是整个集合的GET与分页。 GET / items您可以在自定义标题中返回集合大小和当前页面。标准链接标题中的分页链接:
Link: <https://api.mydomain.com/v1/items?limit=25&offset=25>; rel=next
此方法的问题是,您需要添加引用响应中特定元素的元数据。在这种情况下,只需将其嵌入对象本身。并采用一致的方法......始终将所有元数据添加到响应中。所以回到GET / items,想象每个项目都创建并更新了元数据:
{
items:[
{
"id":"w67e87898dnkwu4752igd",
"message" : "some content",
"_created": "2014-02-14T10:07:39.574Z",
"_updated": "2014-02-14T10:07:39.574Z"
},
......
{
"id":"asjdfiu3748hiuqdh",
"message" : "some other content",
"_created": "2014-02-14T10:07:39.574Z",
"_updated": "2014-02-14T10:07:39.574Z"
}
],
"_total" :133,
"_links" :[
{
"next" :{
href : "https://api.mydomain.com/v1/items?limit=25&offset=25"
}
]
}
请注意,集合响应是一种特殊情况。如果向集合添加元数据,则集合不能再作为数组返回,它必须是包含数组的对象。为什么一个物体?因为您想添加一些元数据属性。
与各个项目中的元数据进行比较。没有任何东西可以包裹实体。您只需向资源添加一些属性即可。
一种惯例是区分控制或元数据字段。您可以使用下划线为这些字段添加前缀。
根据@Charlie的评论:对于问题的分页部分,你仍然需要将元数据烘焙到响应somhow中,但这里的status
和message
属性有些多余,因为它们已经被HTTP
协议本身所覆盖(状态) 200
- 模型发现,404
- 模型未找到,403
- 不足的私人,你明白了)(见spec)。即使您的服务器返回错误条件,您仍然可以将message
部分作为响应正文发送。这两个字段将涵盖您的大部分元数据需求。
就个人而言,我倾向于(ab)使用自定义HTTP标头来获取较小的元数据(带有X-
前缀),但我认为不实用的限制非常低。
我在一个范围较小的问题中对expanded有点了解,但我认为这些问题仍然适用于这个问题。
我们有相同的用例,我们需要在JSON响应中添加分页元数据。我们最终在Backbone中创建了一个可以处理这些数据的集合类型,并在Rails端创建了一个轻量级的包装器。此示例仅将元数据添加到集合对象以供视图参考。
所以我们创建了一个像这样的Backbone Collection类
// Example response:
// { num_pages: 4, limit_value: 25, current_page: 1, total_count: 97
// records: [{...}, {...}] }
PageableCollection = Backbone.Collection.extend({
parse: function(resp, xhr) {
this.numPages = resp.num_pages;
this.limitValue = resp.limit_value;
this.currentPage = resp.current_page;
this.totalCount = resp.total_count;
return resp.records;
}
});
然后我们在Rails端创建了这个简单的类,在用Kaminari分页时发出元数据
class PageableCollection
def initialize (collection)
@collection = collection
end
def as_json(opts = {})
{
:num_pages => @collection.num_pages
:limit_value => @collection.limit_value
:current_page => @collection.current_page,
:total_count => @collection.total_count
:records => @collection.to_a.as_json(opts)
}
end
end
你在这样的控制器中使用它
class ThingsController < ApplicationController
def index
@things = Thing.all.page params[:page]
render :json => PageableCollection.new(@things)
end
end
请享用。希望你觉得它有用。
我建议你阅读本页https://www.odata.org/你不是被迫使用OData,但他们开展工作的方式是REST良好实践的一个很好的例子。
以上是关于将元数据添加到RESTful JSON响应的最佳做法是什么?的主要内容,如果未能解决你的问题,请参考以下文章
SonataUserBundle Api 响应返回空 JSON
Java get GET Restful Service的响应时间
用于POST api请求和响应对象建模的RESTful api最佳实践
Stripe - 在创建结帐会话和付款时/之后将元数据添加到连接/目标帐户中