Rails:NoMethodError(用于_controller的未定义方法_url.创建后似乎无法正确响应json.为什么? [英] Rails: NoMethodError (Undefined method _url for _controller. I can't seem to respond_with json properly after my create. Why?
问题描述
- 列表项
我的config/routes.rb文件...
My config/routes.rb file...
Rails.application.routes.draw do
namespace :api, defaults: {format: 'json'} do
namespace :v1 do
resources :hotels do
resources :rooms
end
end
end
我的app/controllers/api/v1/hotels_controller.rb
My app/controllers/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
当我通过Postman进行POST时,我的数据保存得很好,但是却收到此NoMethodError.为什么是这样?问题似乎发生在第21行,这是response_with(@hotel)行.不仅应该通过show方法使用新创建的酒店的json ouput进行响应吗?
When I go to POST through Postman, my data saves just fine, but I get this NoMethodError. Why is this? The issue seems to be occurring at line 21, which is the respond_with(@hotel) line. Should it not just be responding with json ouput for the newly created hotel, via the show method?
(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/ruby-2.0.0-p451@railstutorial_rails_4_0/gems/actionpack-4.1.0/lib/action_dispatch/middleware/templates/rescues/_source.erb (1.0ms)
Rendered /Users/.rvm/gems/ruby-2.0.0-p451@railstutorial_rails_4_0/gems/actionpack-4.1.0/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb (1.7ms)
Rendered /Users/.rvm/gems/ruby-2.0.0-p451@railstutorial_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/ruby-2.0.0-p451@railstutorial_rails_4_0/gems/actionpack-4.1.0/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb within rescues/layout (31.5ms)
推荐答案
由于您的路由位于API + v1命名空间中,因此您实际上需要在成功创建资源后重定向到api_v1_hotel_url(@hotel)
.当然,这是一个API,并且没有真正的重定向,但是默认的Rails响应器不知道这一点.它还不知道您的路由名称空间.
Because your route is in the API + v1 namespace, you actually need to redirect to the api_v1_hotel_url(@hotel)
after you successfully create your resource. Of course, this is an API and there is no real redirecting, but the default Rails responder doesn't know that. It also doesn't know about your routing namespaces.
仅使用默认响应者,您就必须这样做
With just the default responder, you would have to do
respond_with :api, :v1, @hotel
因此,Rails将构建一个存在的URL.或者,您可以创建一个删除:location
选项的自定义响应器.这是默认的响应者: http://api.rubyonrails.org /files/actionpack/lib/action_controller/metal/responder_rb.html
So that Rails will build a URL that exists. Alternatively, you can create a custom responder that remove the :location
option. Here is the default responder: http://api.rubyonrails.org/files/actionpack/lib/action_controller/metal/responder_rb.html
通读该类的源代码对于理解respond_with
很有帮助.例如,在此响应器上使用respond_with
之前,无需使用if record.save
. Rails将检查记录是否为您成功保存,如果保存失败,则呈现422错误.
Reading through the source code for that class is very helpful in understanding respond_with
. For example, you don't need to use if record.save
before you use respond_with
with this Responder. Rails will check if the record saved successfully for you and render a 422 with errors if it failed to save.
无论如何,您可以看到响应者在其初始值设定项中设置了很多变量:
Anyway, you can see that the responder sets up a lot of variables in it's initializer:
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
如果您将此响应程序子类化,则可以进行如下操作:
If you subclassed this responder, you could make something like this:
class CustomResponder < ActionController::Responder
def initialize(*)
super
@options[:location] = nil
end
end
您可以使用responder=
设置控制器的响应者:
You can set a controller's responder using responder=
:
class AnyController < ActionController::Base
self.responder = CustomResponder
# ...
end
为清楚起见,让我回顾一下:
To be clear, let me recap:
- 使用
respond_with
时,Rails将在成功创建后尝试推断要重定向到的路由.想象一下,您有一个Web UI,可以在其中创建酒店.创建酒店后,您将被重定向到标准Rails流中该酒店的show
页面.这就是Rails在这里要做的. - Rails在推断路线时不理解您的路线名称空间,因此会尝试
hotel_url
-一条不存在的路线! - 在资源前面添加符号将使Rails正确推断路线,在这种情况下为
api_v1_hotel_url
- 在API中,您可以创建一个自定义响应器,该响应器仅将推断的位置设置为
nil
,因为您实际上不需要通过简单的JSON响应重定向到任何地方.自定义响应者也可以在许多其他方式中有用.查看源代码.
- When you use
respond_with
, Rails will try to infer what route to redirect to after a successful create. Imagine you had a web UI where you can create hotels. After a hotel is created, you will be redirected to that hotel'sshow
page in the standard Rails flow. That is what Rails is trying to do here. - Rails does not understand your route namespaces when inferring the route, so it attempts
hotel_url
- a route which does not exist! - Adding symbols in front of the resource will allow Rails to infer the route correctly, in this case
api_v1_hotel_url
- In an API, you can make a custom responder which just sets the inferred location to
nil
, since you don't actually need to redirect anywhere with a simple JSON response. Custom responders can also be useful in many other ways. Check out the source code.
这篇关于Rails:NoMethodError(用于_controller的未定义方法_url.创建后似乎无法正确响应json.为什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!