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

Rails: NoMethodError (Undefined метод _url для _controller.Я не могу ответить response_with json правильно после моего создания. Почему?

  • Элемент списка

Мой файл config/routes.rb...

Rails.application.routes.draw do

  namespace :api, defaults: {format: 'json'} do
    namespace :v1 do
      resources :hotels do
        resources :rooms
      end
    end
  end

Мои приложения/контроллеры/api/v1/hotels_controller.rb

module Api
    module V1
        class HotelsController < ApplicationController
            respond_to :json
            skip_before_filter :verify_authenticity_token

            def index
                @hotels = Hotel.all
                respond_with ({hotels: @hotels}.as_json)
                #respond_with(@hotels)
            end

            def show
                @hotel = Hotel.find(params[:id])
                respond_with (@hotel)
            end

            def create
                @hotel = Hotel.new(user_params)
                if @hotel.save
                    respond_with (@hotel) #LINE 21
                end
            end

            private

                def user_params
                    params.require(:hotel).permit(:name, :rating)
                end
        end
    end
end

Когда я перехожу к POST через Postman, мои данные сохраняются просто отлично, но я получаю этот NoMethodError. Почему это? Проблема, похоже, происходит в строке 21, которая является линией reply_with (@hotel). Если это не просто ответ на json ouput для вновь созданного отеля, то с помощью метода show?

(1.1ms)  COMMIT
Completed 500 Internal Server Error in 76ms

NoMethodError (undefined method `hotel_url' for #<Api::V1::HotelsController:0x0000010332df58>):
  app/controllers/api/v1/hotels_controller.rb:21:in `create'


  Rendered /Users/.rvm/gems/[email protected]_rails_4_0/gems/actionpack-4.1.0/lib/action_dispatch/middleware/templates/rescues/_source.erb (1.0ms)
  Rendered /Users/.rvm/gems/[email protected]_rails_4_0/gems/actionpack-4.1.0/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb (1.7ms)
  Rendered /Users/.rvm/gems/[email protected]_rails_4_0/gems/actionpack-4.1.0/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb (1.4ms)
  Rendered /Users/.rvm/gems/[email protected]_rails_4_0/gems/actionpack-4.1.0/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb within rescues/layout (31.5ms)
4b9b3361

Ответ 1

Поскольку ваш маршрут находится в пространстве имен API + v1, вам действительно нужно перенаправить на api_v1_hotel_url(@hotel) после успешного создания вашего ресурса. Конечно, это API, и нет реального перенаправления, но ответчик Rails по умолчанию этого не знает. Он также не знает о ваших пространствах имен маршрутизации.

Только с ответчиком по умолчанию вам придется делать

respond_with :api, :v1, @hotel

Итак, Rails создаст существующий URL. Кроме того, вы можете создать настраиваемый ответчик, который удаляет параметр :location. Вот ответчик по умолчанию: http://api.rubyonrails.org/files/actionpack/lib/action_controller/metal/responder_rb.html

Чтение исходного кода для этого класса очень полезно для понимания respond_with. Например, вам не нужно использовать if record.save, прежде чем использовать respond_with с этим ответчиком. Rails проверяет, успешно ли сохранена запись для вас и отображает 422 с ошибками, если она не удалось сохранить.

В любом случае, вы можете видеть, что ответчик устанавливает в него много переменных:

def initialize(controller, resources, options={})
  @controller = controller
  @request = @controller.request
  @format = @controller.formats.first
  @resource = resources.last
  @resources = resources
  @options = options
  @action = options.delete(:action)
  @default_response = options.delete(:default_response)
end

Если вы подклассифицируете этого ответчика, вы можете сделать что-то вроде этого:

class CustomResponder < ActionController::Responder
  def initialize(*)
    super
    @options[:location] = nil
  end
end

Вы можете установить ответчика контроллера с помощью responder=:

class AnyController < ActionController::Base
  self.responder = CustomResponder

  # ...
end

Чтобы быть ясным, позвольте мне повторить:

  • Когда вы используете respond_with, Rails попытается сделать вывод о том, какой маршрут перенаправить после успешного создания. Представьте, что у вас есть веб-интерфейс, где вы можете создавать отели. После того, как отель будет создан, вы будете перенаправлены на эту страницу show в стандартном потоке Rails. Вот что делает Rails здесь.
  • Rails не понимает пространства имен маршрутов при выводе маршрута, поэтому он пытается hotel_url - маршрут, который не существует!
  • Добавление символов перед ресурсом позволит Rails правильно выводить маршрут, в этом случае api_v1_hotel_url
  • В API вы можете создать собственный ответчик, который просто устанавливает предполагаемое местоположение в nil, так как на самом деле вам не нужно перенаправлять куда угодно с помощью простого ответа JSON. Пользовательские респонденты также могут быть полезны многими другими способами. Проверьте исходный код.