2012-06-19 2 views
17

Это вопрос «почему он работает таким образом», а не «как мне это сделать».Rails reply_with - почему POST возвращает URL вместо данных?

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

Я использовал Rails 3 respond_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 

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

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

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

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

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

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

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

Ryan Daigle's post о внедрении объясняет, что ожидается то, что происходит.

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

Я не говорю, что это неправильно, только пытается понять обоснование реализации Rails.

+0

Я не уверен, что вы просите/говорите здесь. Вы можете уточнить? Вы ... (1) спрашиваете, почему 'response_with' не работает для вас? (2) заявив, что сторонний API, который вы используете, не возвращает «только» данные и код состояния? (3) спрашивает: «Почему API должен возвращать что-либо, кроме данных?» –

+0

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

+0

Пожалуйста, выведите '@ products' и сообщите нам результат. –

ответ

16

Резюме: Rails получает свое обоснование от HTTP и REST.

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

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

Просто мост от того, что вы читали, что я собираюсь разработать, я хочу упомянуть соответствующие части из Ryan Daigle's article on Default RESTful Rendering:

Если: формат HTML было предложено:

[некоторый текст удален]

  • [после PUT или POST и без каких-либо ошибок проверки] перенаправлять местоположение ресурса (т.е. user_url)

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

Если другой формат, было предложено, (т.е. : XML или: JSON)

[некоторый текст удален]

  • Если запрос был POST, вызовите: to_format метода на ресурсе и отправить его обратно с: созданным статусом и: расположением новый созданный ресурс»

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

  • Для содержания человека (например, HTML), после POST или PUT сервер должен сообщить браузеру перенаправить через 303 на вновь созданный ресурс. Это обычная практика - очень полезная вещь, потому что пользователь хочет увидеть обновления, полученные в результате их редактирования.

  • Для содержимого машины (например, JSON, XML) после PUT сервер должен просто отобразить 201. Клиент, в данном случае, программа, использующая API, может решить остановиться там. (В конце концов, клиент указал запрос и получил 201, так что все это честно.) Именно поэтому используется 201 (успех), а не 303 (перенаправление). Если клиент хочет запросить вновь созданный ресурс, он может найти его с помощью заголовка Location, но перенаправление не должно быть принудительным.

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

Для фона, я поделился немного от W3C Page on 201 Created:

10.2.2 201 Создано

Запрос был выполнен и в результате новый ресурс создается. На вновь созданный ресурс можно ссылаться на URI (ы), возвращенные в сущности ответа, с самым конкретным URI для ресурса, заданного полем заголовка Location. Ответ СЛЕДУЕТ включать объект, содержащий список характеристик и местоположения ресурсов, из которых пользователь или пользователь может выбрать наиболее подходящий. Формат сущности определяется типом носителя, указанным в поле заголовка Content-Type. Исходный сервер ДОЛЖЕН создать ресурс перед возвратом кода состояния 201. Если действие не может быть выполнено немедленно, сервер ДОЛЖЕН ответить на ответ 202 (Accepted).

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

+0

А теперь я понимаю логическое обоснование. Да, имеет смысл, что, как только вы что-то создали, вы хотели бы иметь ссылку на него. Я не уверен, но я думаю, что Реализация 'response_with' предполагает, что метод' to_s' объекта вернет Rails-совместимое имя (я осмотрю объект Rails и узнаю, когда получу минута), чтобы он мог что-то сделать с местоположением. когда вы находитесь в Rails-ориентированном мире, но не в моем случае, когда все, что я хочу от API, это данные, d обрабатывать навигацию таким образом, чтобы ни один API не мог разумно предсказать. Спасибо за помощь! –

+1

Для контента для человека перенаправление также заставляет браузер выполнять запрос GET для отображения результата, который не позволит пользователю повторно загружать свои данные при обновлении браузера. –

1

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

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

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

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

+0

спасибо за все ваши ответы. Я попытался прояснить вопрос, чтобы было ясно, что я понимаю, что происходит, когда вызывается 'response_with'. И у меня есть решение. Мне просто не кажется, что Rails должен работать таким образом, поэтому я хочу понять больше. –

+0

Спасибо за разъяснение. На данный момент я оставлю этот ответ неповрежденным. Я ответил на ваш разъясненный вопрос отдельно. –

+0

@DavidJames, вы заявили: «По определению вызов API должен возвращать структуру данных». Не так. Спецификация RFC, которую вы цитируете в своем ответе за ответ 201, говорит, что «ДОЛЖЕН» вернуться, а не «ДОЛЖНО вернуться», сущность. Существуют некоторые варианты использования, когда 201 без тела является приемлемым (хотя некоторые утверждают, что используют ответ 204 для что так явно нет тела). Моя точка зрения - вызов API не всегда должен возвращать структуру данных. Интересная дискуссия здесь: https://www.codeschool.com/discuss/t/surviving-apis-with -rails-disagreements-and-clarifications/5219 – rmcsharry

2

Ответа на этот вопрос `why 'был удален с помощью @ 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