Rails:NoMethodError(_controller的未定义方法_url。在创建之后我似乎无法正确响应json。为什么?

  1. 项目清单

我的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 

我的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行,即respond_with(@hotel)行。 它是不是应该通过show方法响应新创建的酒店的json输出?

 (1.1ms) COMMIT Completed 500 Internal Server Error in 76ms NoMethodError (undefined method `hotel_url' for #): 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_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非常有帮助。 例如,在对此Responder使用respond_with之前,不需要使用if record.save 。 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=设置控制器的响应responder=

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

为了清楚起见,让我回顾一下:

  1. 当您使用respond_with ,Rails将尝试推断成功创建后重定向到哪条路由。 想象一下,您有一个可以创建酒店的Web UI。 创建酒店后,您将被重定向到标准Rails流程中该酒店的show页面。 这就是Rails在这里尝试做的事情。
  2. 在推断路由时,Rails不了解您的路由命名空间,因此它尝试使用hotel_url - 一条不存在的路由!
  3. 在资源前添加符号将允许Rails正确推断路由,在本例中为api_v1_hotel_url
  4. 在API中,您可以创建一个自定义响应器,它只是将推断的位置设置为nil ,因为您实际上不需要使用简单的JSON响应重定向到任何地方。 自定义响应器在许多其他方面也很有用。 查看源代码。