Вопрос по rest, respond-to, ruby-on-rails-3, json – Rails response_with - почему POST возвращает URL вместо данных?

17

Это вопрос «почему это работает так», а не «как мне заставить это работать».

Мое приложение вызывает сторонний REST API, который возвращает JSON, и возвращает результат как часть моегоown JSON API.

Я использовал Rails 3respond_to а такжеrespond_with методы; в случаеGET запросы, это работает, как я ожидаю, просто проходя через JSON.

В случаеPOST, он делает больше, включая создание URL-адреса из объекта, возвращенного для передачи в:location вариант. Но так как мой объект просто JSON (не ActiveRecord), я получаю сообщение об ошибке.

Например...

# POST /api/products.json with params id=:id
def create
  query_string = "#{user_id}&id=#{params[:id]}"
  @products = third_party_api_wrapper.products(query_string, 'POST')
  respond_with @products
end 

Моя оболочка для стороннего API делает запрос POST, который возвращается нормально, а затем Rails возвращает ошибку 500, которая регистрируется следующим образом:

NoMethodError (undefined method `{"response":{"message":"product 4e1712d9ec0f257c510013f8 selected"}}_url' for #<MyController> 

Rails хочет, чтобы объект my @products знал, как создать URL-адрес местоположения.

РАЗЪЯСНЕНИЕ:@products Объектом, возвращаемым сторонним API, является чистый JSON - строка, которую вы можете увидеть в сообщении об ошибке выше. Эта ошибка возникает из-за того, что Rails, кажется, хочет, чтобы объект был чем-то большим - во внутренней поддержке API Rails это объект ActiveRecord.

Если я заменю новыйrespond_with с синтаксисом в старом стиле

respond_to do |format|
  format.json { render :json => @products }  # note, no :location or :status options
end

тогда все работает. И это то, что я сделал, поэтому у меня нет «как» проблема, вместо этого есть «почему» вопрос.

Сообщение Райана Дейгла во введении объясняется, что то, что происходит, ожидается.

Мой вопрос:why делаетrespond_with ожидать ничего, кроме данных (и статуса HTTP?), и, видимо, только дляPOST.

Я не говорю, что это неправильно, простоtrying to understand обоснование реализации Rails.

Пожалуйста, выведите@products и сообщите нам результат. David J.
Если ваш главный вопрос - «почему API должен возвращать что-либо, кроме данных (и статуса HTTP?). Я не говорю, что это неправильно, просто пытаюсь понять причину ». возможно, это поможет нам узнать больше об API. David J.
Я не уверен, что вы спрашиваете / говорите здесь. Вы можете уточнить? Вы ... (1) спрашиваете почемуrespond_with у тебя не сработало? (2) говорят, что используемый вами сторонний API не возвращает "просто" данные и код состояния? (3) спросить "почему API должен возвращать что-либо, кроме данных?" David J.

Ваш Ответ

3   ответа
1

Чтобы ответить на этот вопрос: «Почему API должен возвращать что-либо, кроме данных (и статуса HTTP?). Я не говорю, что это неправильно, просто пытаюсь понять причину ».

Я не могу придумать ни одного хорошего объяснения. Что еще более важно, я не вижу никакого способа, чтобы API мог вернуть что-либоexcept структура данных! (Этот вопрос не имеет смысла для меня!)

По определению, вызов APImust вернуть структуру данных. (Это может быть просто строка. Это может быть JSON. Это может быть XML.) Он может использовать согласование содержимого для выбора формата. Это может быть или не быть строгая схема, но, по крайней мере, клиентская библиотека должна быть в состоянии проанализировать ее. В любом случае, документация API должна сделать это совершенно ясно и придерживаться этого. Как еще клиентские библиотеки могут ожидать взаимодействия?

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

спасибо за все ваши ответы. Я попытался прояснить вопрос, чтобы понять, что я понимаю, что происходит, когдаrespond_with называется. И у меня есть решение. Мне просто не имеет смысла, что Rails должен работать таким образом, поэтому я хочу понять больше. Tom Harrison Jr
Спасибо за разъяснения. Я собираюсь пока оставить этот ответ без изменений. Я ответил на ваш уточненный вопрос отдельно.
@DavidJames, вы заявили: «По определению вызов API должен возвращать структуру данных». Не так. Спецификация RFC, которую вы цитируете в своем ответе на 201 ответ, гласит: «СЛЕДУЕТ». возвращать, а не "ДОЛЖЕН возвращаться" объект. В некоторых случаях можно использовать 201 без тела (хотя некоторые утверждают, что для этого нужно использовать ответ 204, поэтому явно нет тела). Моя точка зрения - вызов API не всегда должен возвращать структуру данных. Интересная дискуссия здесь:codeschool.com/discuss/t/…
2

«Почему» превосходно ответил @ david-james. Это всего лишь краткое "как" ответить черезrespond_with:

class Api::V1::UsersController < ApplicationController

  respond_to :json

  def create
    @user = User.create(...)
    respond_with @user, location: url_for([:api, :v1, @user])
  end

end
16

Summary: Rails gets its rationale from HTTP and REST.

(Спасибо за ваш обновленный вопрос. Теперь я понимаю ваш основной вопрос: "Я не говорю, что это неправильно", просто пытаюсь понять обоснование реализации Rails. ")

Теперь для объяснения. Обоснование того, как ведет себя Rails, коренится в принятии соглашений HTTP и REST.

Просто чтобы соединить то, что вы прочитали, с тем, что я собираюсь уточнить, я хочу упомянуть соответствующие части изСтатья Райана Дейгла о RESTful-рендеринге по умолчанию:

If the :html format was requested:

[some text removed]

  • [after PUT or POST and no validation errors] redirect to the resource location (i.e. user_url)

(Текст [в скобках] был добавлен мной.)

If another format was requested, (i.e. :xml or :json)

[some text removed]

  • If it was a POST request, invoke the :to_format method on the resource and send that back with the :created status and the :location of the new created resource"

Позвольте мне выразить это словами о том, что Rails считает хорошей практикой:

  • For human content (e.g. HTML), after a POST or PUT, the server should tell the browser to redirect via a 303 to the newly created resource. This is common practice -- a very useful thing because a user wants to see the updates resulting from their edits.

  • For machine content (e.g. JSON, XML), after a PUT, the server should just render a 201. The client, in this case, a program consuming an API, might decide to stop there. (After all, the client specified the request and got a 201, so all is honky dory.) This is why 201 (success), not 303 (redirect), is used. If the client wants to request the newly created resource, it can look it up using the Location header -- but a redirect should not be forced.

В любом случае обратите внимание, что местоположение вновь созданного ресурса не требуется. Вот почему@products в приведенном выше примере должны содержать как данные, так и местоположение.

Для справки я немного поделилсяW3C Страница на 201 Создана:

10.2.2 201 Created

The request has been fulfilled and resulted in a new resource being created. The newly created resource can be referenced by the URI(s) returned in the entity of the response, with the most specific URI for the resource given by a Location header field. The response SHOULD include an entity containing a list of resource characteristics and location(s) from which the user or user agent can choose the one most appropriate. The entity format is specified by the media type given in the Content-Type header field. The origin server MUST create the resource before returning the 201 status code. If the action cannot be carried out immediately, the server SHOULD respond with 202 (Accepted) response instead.

Я надеюсь, что это помогает объяснить обоснование. Я (наивно?) Понимаю, что это обоснование хорошо воспринимается в веб-фреймворках. Исторически сложилось так, что я подозреваю, что Rails был яркой основой для реализации (новое слово оповещение!) Для многих ярых сторонников REST и ресурс-ориентированной архитектуры.

Ах, теперь я понимаю обоснование. Да, имеет смысл, что, как только вы что-то создали, вы захотите сослаться на это. Я не уверен, но я думаю, чтоrespond_with реализация предполагает, что объектto_s Метод возвращает совместимое с Rails-маршрутом имя (я проверю объект Rails и выясню, когда у меня будет минута), так что он может что-то делать с местоположением. Это прекрасно работает, когда вы находитесь в мире, ориентированном на Rails, но не в моем случае, когда все, что я хочу от API - это данные, и я управляю навигацией так, как ни один API не мог разумно предсказать. Спасибо за помощь! Tom Harrison Jr
Для человеческого контента перенаправление также заставляет браузер выполнить запрос GET для отображения результата, который не позволит пользователю повторно разместить свои данные при обновлении браузера.

Похожие вопросы