背景
我们正在构建一个 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.exbackbone.js 中使用响应而不提取数据对象。
问题
将元数据添加到 json 响应的最佳实践是什么?
更新
到目前为止,我从下面的答案中得到了什么:
- 删除
metadata.status
并返回http响应代码
改为 http 协议 (200, 500 ...)
- 将错误消息添加到 http 500 响应的正文
- 对于分页,我自然会拥有一些元数据来告诉分页结构,以及嵌套在该结构中的数据
- 可以添加少量元数据到http标头(X-something)
Background
We are building a Restful API that should return data objects as JSON. In most of the cases it fine just to return the data object, but in some cases, f.ex. pagination or validation, we need to add some metadata to the response.
What we have so far
We have wrapped all json responses like this example:
{
"metadata" :{
"status": 200|500,
"msg": "Some message here",
"next": "http://api.domain.com/users/10/20"
...
},
"data" :{
"id": 1001,
"name": "Bob"
}
}
Pros
- We can add helpful metadata to the response
Cons
- In most cases we don't need the metadata field, and it adds complexity to the json format
- Since it's not a data object any more, but more like a enveloped response, we can not use the response right away in f.ex backbone.js without extracting the data object.
Question
What is the best practices to add metadata to a json response?
UPDATE
What I've got so far from answers below:
- Remove the
metadata.status
an return the http response code in the
http protocol instead (200, 500 ...)
- Add error msg to body of an http 500 repsonse
- For pagination i natural to have some metadata telling about the pagination structure, and the data nested in that structure
- Small amount of meta data can be added to http header (X-something)
发布评论
评论(6)
您可以通过多种方式在 RESTful API 中传递元数据:
对于metadata.status,请使用 Http 状态代码,这就是用途!
如果元数据是指整个响应,您可以将其添加为标头字段。
如果元数据仅引用响应的一部分,则必须将元数据作为对象的一部分嵌入。不要将整个响应包装在人造信封中,并将包装器拆分为数据和元数据。
最后,在整个 API 中保持一致与您所做的选择。
一个很好的例子是对整个集合进行 GET 分页。获取/项目
您可以返回集合大小和自定义标题中的当前页面。标准链接标头中的分页链接:
此方法的问题是当您需要在响应中添加引用特定元素的元数据时。在这种情况下,只需将其嵌入到对象本身中即可。为了采用一致的方法...始终将所有元数据添加到响应中。因此,回到 GET /items,假设每个项目都已创建并更新了元数据:
请注意,集合响应是一种特殊情况。如果将元数据添加到集合中,则该集合不能再作为数组返回,它必须是一个包含数组的对象。为什么是一个物体?因为您想添加一些元数据属性。
与各个项目中的元数据进行比较。没有什么接近包裹实体。您只需向资源添加一些属性即可。
一种约定是区分控制或元数据字段。您可以在这些字段前添加下划线。
You have several means to pass metadata in a RESTful API:
For the metadata.status, use the Http Status Code, that's what's for!
If metadata is refers to the whole response you could add it as header fields.
If metadata refers only to part of the response, you will have to embed the metadata as part of the object.DON'T wrap the whole response in an artifical envelope and split the wrapper in data and metadata.
And finally, be consistent across your API with the choices you make.
A good example is a GET on a whole collection with pagination. GET /items
You could return the collection size, and current page in custom headers. And pagination links in standard Link Header:
The problem with this approach is when you need to add metadata referencing specific elements in the response. In that case just embed it in the object itself. And to have a consistent approach...add always all metadata to response. So coming back to the GET /items, imagine that each item has created and updated metadata:
Note that a collection response is an special case. If you add metadata to a collection, the collection can no longer be returned as an array, it must be an object with an array in it. Why an object? because you want to add some metadata attributes.
Compare with the metadata in the individual items. Nothing close to wrapping the entity. You just add some attributes to the resource.
One convention is to differentiate control or metadata fields. You could prefix those fields with an underscore.
按照@Charlie的评论:对于问题的分页部分,您仍然需要将元数据烘焙到响应中,但是这里的
status
和message
属性有点多余的,因为它们已经被HTTP
协议本身覆盖(状态200
- 找到模型,404
- 未找到模型,403
- 不足privs,你明白了)(参见规范)。即使您的服务器返回错误条件,您仍然可以将message
部分作为响应正文发送。这两个字段将满足您的大部分元数据需求。就我个人而言,我倾向于 (ab) 使用自定义 HTTP 标头来处理较小的元数据(带有
X-
前缀),但我认为不切实际的限制非常低。我已经扩展了一点这是一个范围较小的问题,但我认为这些观点对于这个问题仍然有效。
Along the lines of @Charlie's comment: for the pagination part of your question you still need to bake the metadata into the response somhow, but the
status
andmessage
attributes here are somewhat redundant, since they are already covered by theHTTP
protocol itself (status200
- model found,404
- model not found,403
- insufficient privs, you get the idea) (see spec). Even if your server returns an error condition you can still send themessage
part as the response body. These two fields will cover quite much of your metadata needs.Personally, I have tended towards (ab)using custom HTTP headers for smaller pieces of metadata (with an
X-
prefix), but I guess the limit where that gets unpractical is pretty low.I've expanded a bit about this in a question with a smaller scope, but I think the points are still valid for this question.
我建议您阅读此页面 https://www.odata.org/ 您并非被迫使用OData 但他们的工作方式是 REST 良好实践的一个很好的例子。
I suggest you to read this page https://www.odata.org/ You are not forced to use OData but the way they do the work is a good example of good practice with REST.
我们有相同的用例,需要将分页元数据添加到 JSON 响应。我们最终在 Backbone 中创建了一个可以处理这些数据的集合类型,并在 Rails 端创建了一个轻量级包装器。此示例只是将元数据添加到集合对象中以供视图引用。
所以我们创建了一个像这样的 Backbone Collection 类
然后我们在 Rails 端创建了这个简单的类,以便在使用 Kaminari 分页时发出元数据
您可以在像这样的控制器中使用它
享受。希望您觉得它有用。
We had the same use case, in which we needed to add pagination metadata to a JSON response. We ended up creating a collection type in Backbone that could handle this data, and a lightweight wrapper on the Rails side. This example just adds the meta data to the collection object for reference by the view.
So we created a Backbone Collection class something like this
And then we created this simple class on the Rails side, to emit the meta data when paginated with Kaminari
You use it in a controller like this
Enjoy. Hope you find it useful.
如何直接返回您想要的数据对象,例如 return:
并在标头中返回元数据。
选项 1(所有元数据 JSON 一个标头):
选项 2(每个元数据字段一个标头):
到目前为止,我和您一样使用一个包含两个字段的复杂 JSON,一个用于数据,一个用于元数据。但我想开始使用我建议的这种方式,我认为它会更容易。
请注意,某些服务器对 HTTP 标头有大小限制,例如: https://www.tutorialspoint.com/What-is-the-maximum-size-of-HTTP-header-values
How about returning directly the object that you want in data, like return:
And return in headers the metadata.
Option 1 (one header for all metadata JSON):
Option 2 (one header per each metadata field):
Until now I was using like you, a complex JSON with two fields, one for data and one for metadata. But I'm thinking in starting using this way that I suggested, I think it will be more easy.
Remind that some server have size limit for HTTP headers, like this example: https://www.tutorialspoint.com/What-is-the-maximum-size-of-HTTP-header-values
JSON:API 通过定义顶级
meta
和data 解决了这个问题
属性。JSON:API solves this by defining top-level
meta
anddata
properties.