Подтвердить что ты не робот

Каковы наилучшие методы добавления метаданных в ответ RESTful JSON?

Фон

Мы создаем Restful API, который должен возвращать объекты данных как JSON. В большинстве случаев это просто, чтобы вернуть объект данных, но в некоторых случаях f.ex. pagination или validation, нам нужно добавить некоторые метаданные в ответ.

Что мы до сих пор

Мы завернули все ответы json, как этот пример:

{
    "metadata" :{
        "status": 200|500,
        "msg": "Some message here",
        "next": "http://api.domain.com/users/10/20"
        ...
    },
    "data" :{
        "id": 1001,
        "name": "Bob"
    }
}

Pros

  • Мы можем добавить полезные ответы на ответ

против

  • В большинстве случаев нам не нужно поле метаданных, и это добавляет сложности в формат json
  • Поскольку это не объект данных больше, а скорее похож на окутанный ответ, мы не можем сразу использовать ответ в f.ex backbone.js без извлечения объекта данных.

Вопрос

Каковы наилучшие методы добавления метаданных в ответ json?

UPDATE

Что у меня осталось от ответов ниже:

  • Удалите metadata.status, чтобы вернуть код ответа http в http protocol (200, 500...)
  • Добавить ошибку msg в тело ответа 500 500
  • Для pagination я естественно иметь некоторые метаданные, рассказывающие о структуре разбиения на страницы, и данные, вложенные в эту структуру
  • Небольшое количество метаданных может быть добавлено в заголовок http (X-something)
4b9b3361

Ответ 1

У вас есть несколько способов передать метаданные в RESTful API:

  • Код состояния Http
  • Заголовки
  • Тело ответа

Для metadata.status используйте код состояния Http, для чего! Если метаданные относятся ко всему ответу, вы можете добавить его как поля заголовка. Если метаданные относятся только к части ответа, вам нужно будет внедрить метаданные как часть объекта. НЕ НЕ обернуть весь ответ в искусственном конверте и разделить обертку на данные и метаданные.

И, наконец, будет согласован в вашем 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"
         } 
   ]
}

Обратите внимание, что ответ коллекции является частным случаем. Если вы добавляете метаданные в коллекцию, сбор больше не может быть возвращен как массив, это должен быть объект с массивом в нем. Почему объект? потому что вы хотите добавить некоторые атрибуты метаданных.

Сравните с метаданными в отдельных элементах. Ничто не близко к обертыванию объекта. Вы просто добавляете некоторые атрибуты в ресурс.

Одно из условий - отличать поля управления или метаданных. Вы можете префикс этих полей с подчеркиванием.

Ответ 2

Вдоль строк комментария @Charlie: для фрагментации вашего вопроса вам все равно нужно выпекать метаданные в ответ так или иначе, но атрибуты status и message здесь несколько избыточны, так как они уже покрыты по самому протоколу HTTP (статус 200 - найденная модель, 404 - модель не найдена, 403 - недостаточно привилегий, вы получаете эту идею) (см. spec). Даже если ваш сервер возвращает условие ошибки, вы все равно можете отправить часть message в качестве тела ответа. Эти два поля будут покрывать большую часть ваших потребностей в метаданных.

Лично я склонялся к (ab) с помощью пользовательских заголовков HTTP для небольших фрагментов метаданных (с префиксом X-), но я предполагаю, что предел, где это становится непрактичным, довольно низок.

несколько расширился в вопросе с меньшим объемом, но я думаю, что пункты по-прежнему актуальны для этого вопроса.

Ответ 3

У нас был тот же вариант использования, в котором нам нужно было добавить метаданные разбиения на страницы на ответ 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

Enjoy. Надеюсь, вы сочтете это полезным.