Поддержка нескольких типов контента JSON в Grape

Я использовал Grape для написания API, похожего на Evernote, и использовал Collection+JSON (тип MIME «application/vnd.collection+json»). Теперь я хочу также поддерживать JSON API (тип пантомимы «application/vnd.api+json»).

(Причина, по которой я хочу это сделать, заключается в том, что будет проще написать клиент Ember, поскольку Ember Data имеет встроенную поддержку JSON API. Возможно, для решения моей проблемы имеет смысл написать клиентский JavaScript, но я m сильнее в Ruby, чем в JavaScript.)

Моя цель состоит в том, чтобы сервер поддерживал любой формат и выбирал, какой из них основан на заголовке Accept. Это разумно, или нет смысла иметь несколько типов JSON? Похоже, что Grape не поддерживает это. Если у меня просто неправильное представление, то приведенный ниже код, вероятно, не имеет отношения к этому ответу.

Вот некоторые важные фрагменты кода моего сервера:

class NotesServer < Grape::API
  content_type :json, ‘application/json’


  content_type :json_api, 'application/vnd.api+json'
  formatter :json_api, lambda { |object, env| object.to_json_api }

  content_type :collection_json, 'application/vnd.collection+json'
  formatter :collection_json, lambda { |object, env| object.to_collection_json }

  resource :notes do
    desc 'Get a note.'

    params do
      requires :id, type: Integer, desc: 'Note ID.'
    end

    route_param :id do
      get do
        NoteRepresenter.new Note.find(params[:id])
      end
    end
end

…где NoteRepresenter определяет как to_json_api, так и to_collection_json.

Идея состоит в том, чтобы использовать один и тот же представитель в любом случае, но вызывать соответствующий метод в зависимости от заголовка Accept.

Когда я запускаю curl для этого с любым заголовком Accept, ответ имеет Content-Type: application/json, а на репрезентаторе вызывается «to_json». Если я пропущу строку content_type :json, ‘application/json’, я каждый раз получаю ответ 406 Not Acceptable.

Я выяснил, что в библиотеке Grape, в lib/grape/middleware/formatter.rb, "формат" запроса считается JSON для любого из этих типов MIME, так как части vnd.collection+ или vnd.api+ разбираются заголовка. Итак, мой вопрос: дело в том, что Грейп не поддерживает то, что я пытаюсь сделать, или я пытаюсь сделать что-то бессмысленное?


person Kirkland    schedule 14.01.2015    source источник
comment
Почему тип application/vnd.api+json, а не application/json?   -  person tadman    schedule 14.01.2015
comment
Я совсем не знаком с Grape, но поддержка нескольких типов контента на основе заголовка Accept — это определенно HTTP/стандартный способ сделать это, а также разумный подход/цель.   -  person Pete    schedule 14.01.2015
comment
@tadman Я пытаюсь использовать стандарт, задокументированный здесь: jsonapi.org и аналогично для Collection+JSON здесь: amundsen.com/media-types/collection   -  person Kirkland    schedule 15.01.2015
comment
Возможно, вам придется вставить обработчик Rack, который сопоставляет их с обычным JSON или правильно их декодирует.   -  person tadman    schedule 15.01.2015


Ответы (1)


Я нашел разумный способ сделать это, которым я вполне доволен, но я не думаю, что он полностью соответствует тому, как Грейп ожидает от вас действий.

Я создал собственный модуль форматирования под названием JsonFormatter, поэтому код моего сервера выглядит так:

class NotesServer < Grape::API
  formatter :json, JsonFormatter
  # …
end

И сам форматер:

module JsonFormatter
  def self.call(object, env)
    case env['HTTP_ACCEPT']
    when 'application/vnd.api+json'
      object.to_json_api
    when 'application/vnd.collection+json'
      object.to_collection_json
    else
      object.to_json
    end
  end
end

Так что теперь либо to_json_api, либо to_collection_api (или to_json) будут вызываться для объекта-презентатора, в зависимости от заголовка Accept. Одна проблема, которую я еще не решил, заключается в том, что для Content-Type во всех случаях установлено значение application/json.

person Kirkland    schedule 21.01.2015