将元数据添加到RESTful JSON响应的最佳做法是什么?

时间:2011-11-28 10:10:24

标签: java json http rest backbone.js

背景

我们正在构建一个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响应的最佳做法是什么?

更新

到目前为止,我从以下答案中得到了什么:

  • 删除metadata.status并返回http响应代码 http协议(200,500 ...)
  • 将错误消息添加到http 500 repsonse的正文
  • 对于分页我自然会有一些元数据告诉分页结构,以及嵌套在该结构中的数据
  • 可以将少量元数据添加到http标头(X-something)

5 个答案:

答案 0 :(得分:9)

您有多种方法可以在RESTful API中传递元数据:

  1. Http状态代码
  2. 接头
  3. 回复正文
  4. 对于metadata.status,请使用Http状态代码,这就是为什么! 如果元数据是指整个响应,则可以将其添加为标题字段。 如果元数据仅涉及响应的一部分,则必须将元数据作为对象的一部分嵌入。不要将整个响应包装在人工信封中,并将包装器拆分为数据和元数据。

    最后,在您的API中保持一致与您做出的选择。

    一个很好的例子是整个集合的GET与分页。 GET /项目 您可以在自定义标题中返回集合大小和当前页面。标准链接标题中的分页链接:

    Link: <https://api.mydomain.com/v1/items?limit=25&offset=25>; rel=next
    

    此方法的问题在于您需要添加引用响应中特定元素的元数据。在这种情况下,只需将其嵌入对象本身。并采用一致的方法......始终将所有元数据添加到响应中。因此,回到GET /项目,想象每个项目都创建并更新了元数据:

    {
      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"
             } 
       ]
    }
    

    请注意,集合响应是一种特殊情况。如果向集合添加元数据,则集合不能再作为数组返回,它必须是包含数组的对象。为什么一个物体?因为你想添加一些元数据属性。

    与各个项目中的元数据进行比较。没有任何东西可以包裹实体。您只需向资源添加一些属性。

    一种惯例是区分控制或元数据字段。您可以使用下划线为这些字段添加前缀。

答案 1 :(得分:4)

与@Charlie的评论一致:对于问题的分页部分,您仍然需要将元数据烘焙到响应somhow中,但此处的statusmessage属性有些多余,因为它们已被HTTP协议本身所覆盖(状态200 - 已找到模型,404 - 未找到模型,403 - 权限不足,您明白了这一点(参见参考资料) spec)。即使您的服务器返回错误条件,您仍然可以将message部分作为响应正文发送。这两个字段将涵盖您的元数据需求。

就个人而言,我倾向于(ab)使用自定义HTTP标头来获取较小的元数据(带有X-前缀),但我认为不实用的限制非常低。

我在一个范围较小的问题中对此expanded有点了解,但我认为这些问题对这个问题仍有效。

答案 2 :(得分:0)

我们有相同的用例,我们需要在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

享受。希望你觉得它很有用。

答案 3 :(得分:0)

我建议您阅读此页面https://www.odata.org/并不一定要使用OData,但是他们的工作方式是REST良好实践的一个很好的例子。

答案 4 :(得分:0)

直接返回数据中想要的对象怎么样,比如return:

{
    "id": 1001,
    "name": "Bob"
}

并在标题中返回元数据。

选项 1(所有元数据 JSON 的一个标头):

X-METADATA = '{"status": 200|500,"msg": "Some message here","next": "http://api.domain.com/users/10/20"...}'

选项 2(每个元数据字段一个标题):

X-METADATA-STATUS = 200|500
X-METADATA-MSG = "Some message here",
X-METADATA-NEXT = "http://api.domain.com/users/10/20"
...

直到现在我都像你一样使用一个复杂的 JSON,它有两个字段,一个用于数据,一个用于元数据。但是我正在考虑开始使用我建议的这种方式,我认为它会更容易。


提醒某些服务器对 HTTP 标头有大小限制,例如:https://www.tutorialspoint.com/What-is-the-maximum-size-of-HTTP-header-values

相关问题