Rails 无法为 nil:NilClass 呈现 JSON 未定义的“新”方法
Rails can't render JSON undefined method `new' for nil:NilClass
背景
我正在制作一个 Rails 应用程序,它有两个命名空间,分别是 admin
(供管理员使用)和 api
供移动和 Web 客户端使用。
昨天发生了一件奇怪的事。我在我的 PostgreSQL 数据库中创建了一个新的 table facilities
,其中包含 id
、name
、created_at
、updated_at
。
问题
我尝试使用 admin
命名空间 http://localhost:3000/admin/facilities 获取所有设施,并且效果很好(return HTML 和设施列表)。
Started GET "/admin/facilities" for ::1 at 2015-05-10 16:12:47 +0800
Processing by Admin::FacilitiesController#index as HTML
Facility Load (0.4ms) SELECT "facilities".* FROM "facilities"
Rendered admin/facilities/index.html.erb within layouts/application (2.5ms)
Completed 200 OK in 91ms (Views: 90.4ms | ActiveRecord: 0.4ms)
但是当我从浏览器调用 http://localhost:3000/api/v1/facilities 时,它应该是 return JSON。我收到一个错误
Api::V1::FacilitiesController#index 中的 NoMethodError
nil:NilClass
的未定义方法“新”
Extracted source (around line #8):
6 def index
7 @facilities = Facility.all
8 render json: @facilities
9 end
10
11
来自控制台的错误
Started GET "/api/v1/facilities" for ::1 at 2015-05-10 16:38:26 +0800
Processing by Api::V1::FacilitiesController#index as HTML
User Load (0.6ms) SELECT "users".* FROM "users" WHERE "users"."id" IS NULL ORDER BY "users"."id" ASC LIMIT 1
Facility Load (0.5ms) SELECT "facilities".* FROM "facilities"
Completed 500 Internal Server Error in 6ms (ActiveRecord: 1.1ms)
NoMethodError (undefined method `new' for nil:NilClass):
app/controllers/api/v1/facilities_controller.rb:8:in `index'
Rendered /Users/abrahamks/.rvm/gems/ruby-2.2.1/gems/actionpack-4.2.1/lib/action_dispatch/middleware/templates/rescues/_source.erb (7.1ms)
Rendered /Users/abrahamks/.rvm/gems/ruby-2.2.1/gems/actionpack-4.2.1/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb (2.7ms)
. . . .
. . . .
and so on
我从 rails c
检查并调用 Facility.all
看起来命令行 return 正确的值,但我不明白为什么它不能渲染 / return json.
Loading development environment (Rails 4.2.1)
2.2.1 :001 > Facility.all
Facility Load (0.5ms) SELECT "facilities".* FROM "facilities"
=> #<ActiveRecord::Relation [#<Facility id: 1, name: "Kitchen", created_at: "2015-05-09 10:54:00", updated_at: "2015-05-09 16:08:48">, #<Facility id: 2, name: "Washer", created_at: "2015-05-09 11:20:40", updated_at: "2015-05-09 16:09:32">, #<Facility id: 3, name: "Swimming Pool", created_at: "2015-05-09 11:22:19", updated_at: "2015-05-09 16:09:41">, #<Facility id: 4, name: "Internet", created_at: "2015-05-09 11:24:02", updated_at: "2015-05-09 16:12:31">, #<Facility id: 5, name: "Dryer", created_at: "2015-05-09 15:55:36", updated_at: "2015-05-09 16:12:54">]>
我的代码有什么问题?有没有我错过的配置?
谢谢。
如果需要更多详细信息
#获取/facilities/1
facilities
和properties
是多对多的关系,我生成一个jointable
class CreateJoinTablePropertiesFacilities < ActiveRecord::Migration
def change
create_join_table :properties, :facilities do |t|
t.index [:property_id, :facility_id]
t.index [:facility_id, :property_id]
end
end
end
这是我的 facility.rb
class Facility < ActiveRecord::Base
has_and_belongs_to_many :property, join_table: :facilites_properties
end
这是我的 routes.rb
Rails.application.routes.draw do
namespace :admin do
resources :facilities
end
namespace :api do
namespace :v1 do
resources :properties
resources :facilities, only: [:index, :show]
resources :users do
member do
post 'list', :to => "users#list_property"
end
end
这是我的 app/controllers/api/v1/facilities_controller
class Api::V1::FacilitiesController < ApplicationController
before_action :set_facility, only: [:show, :edit, :update, :destroy]
# before_action :authenticate
# GET /facilities
# GET /facilities.json
def index
@facilities = Facility.all
render json: @facilities
end
# GET /facilities/1
# GET /facilities/1.json
def show
render json: @facility
end
private
# Use callbacks to share common setup or constraints between actions.
def set_facility
@facility = Facility.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def facility_params
params.require(:facility).permit(:name)
end
end
这是app/controllers/admin/facilities_controller
class Admin::FacilitiesController < ApplicationController
before_action :set_facility, only: [:show, :edit, :update, :destroy]
# before_action :authenticate
# GET /facilities
# GET /facilities.json
def index
@facilities = Facility.all
end
# GET /facilities/1
# GET /facilities/1.json
def show
end
# GET /facilities/new
def new
@facility = Facility.new
end
# GET /facilities/1/edit
def edit
end
# POST /facilities
# POST /facilities.json
def create
@facility = Facility.new(facility_params)
respond_to do |format|
if @facility.save
format.html { redirect_to admin_facilities_path, notice: 'Facility was successfully created.' }
format.json { render :show, status: :created, location: @facility }
else
format.html { render :new }
format.json { render json: @facility.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /facilities/1
# PATCH/PUT /facilities/1.json
def update
respond_to do |format|
if @facility.update(facility_params)
format.html { redirect_to admin_facilities_path, notice: 'Facility was successfully updated.' }
format.json { render :show, status: :ok, location: @facility }
else
format.html { render :edit }
format.json { render json: @facility.errors, status: :unprocessable_entity }
end
end
end
# DELETE /facilities/1
# DELETE /facilities/1.json
def destroy
@facility.destroy
respond_to do |format|
format.html { redirect_to facilities_url, notice: 'Facility was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_facility
@facility = Facility.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def facility_params
params.require(:facility).permit(:name)
end
end
我才意识到我忘记生成序列化程序了。由于我使用 gem Active Model Serializers,这就是我生成序列化程序所做的。
rails g serializer facility
堆栈跟踪显示请求的处理方式为 HTML。
Processing by Admin::FacilitiesController#index as HTML
当您尝试呈现 JSON 时,您可以指定控制器响应 json
module Api
module V1
class FacilitiesController < ApplicationController
respond_to :json
end
end
end
或者您可以将 json 指定为 routes.rb 文件中的默认响应
namespace :api, defaults: { format: 'json' } do
# your api json routes...
end
希望其中任何一个对您有所帮助。
背景
我正在制作一个 Rails 应用程序,它有两个命名空间,分别是 admin
(供管理员使用)和 api
供移动和 Web 客户端使用。
昨天发生了一件奇怪的事。我在我的 PostgreSQL 数据库中创建了一个新的 table facilities
,其中包含 id
、name
、created_at
、updated_at
。
问题
我尝试使用 admin
命名空间 http://localhost:3000/admin/facilities 获取所有设施,并且效果很好(return HTML 和设施列表)。
Started GET "/admin/facilities" for ::1 at 2015-05-10 16:12:47 +0800
Processing by Admin::FacilitiesController#index as HTML
Facility Load (0.4ms) SELECT "facilities".* FROM "facilities"
Rendered admin/facilities/index.html.erb within layouts/application (2.5ms)
Completed 200 OK in 91ms (Views: 90.4ms | ActiveRecord: 0.4ms)
但是当我从浏览器调用 http://localhost:3000/api/v1/facilities 时,它应该是 return JSON。我收到一个错误
Api::V1::FacilitiesController#index 中的 NoMethodError nil:NilClass
的未定义方法“新”Extracted source (around line #8):
6 def index
7 @facilities = Facility.all
8 render json: @facilities
9 end
10
11
来自控制台的错误
Started GET "/api/v1/facilities" for ::1 at 2015-05-10 16:38:26 +0800
Processing by Api::V1::FacilitiesController#index as HTML
User Load (0.6ms) SELECT "users".* FROM "users" WHERE "users"."id" IS NULL ORDER BY "users"."id" ASC LIMIT 1
Facility Load (0.5ms) SELECT "facilities".* FROM "facilities"
Completed 500 Internal Server Error in 6ms (ActiveRecord: 1.1ms)
NoMethodError (undefined method `new' for nil:NilClass):
app/controllers/api/v1/facilities_controller.rb:8:in `index'
Rendered /Users/abrahamks/.rvm/gems/ruby-2.2.1/gems/actionpack-4.2.1/lib/action_dispatch/middleware/templates/rescues/_source.erb (7.1ms)
Rendered /Users/abrahamks/.rvm/gems/ruby-2.2.1/gems/actionpack-4.2.1/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb (2.7ms)
. . . .
. . . .
and so on
我从 rails c
检查并调用 Facility.all
看起来命令行 return 正确的值,但我不明白为什么它不能渲染 / return json.
Loading development environment (Rails 4.2.1)
2.2.1 :001 > Facility.all
Facility Load (0.5ms) SELECT "facilities".* FROM "facilities"
=> #<ActiveRecord::Relation [#<Facility id: 1, name: "Kitchen", created_at: "2015-05-09 10:54:00", updated_at: "2015-05-09 16:08:48">, #<Facility id: 2, name: "Washer", created_at: "2015-05-09 11:20:40", updated_at: "2015-05-09 16:09:32">, #<Facility id: 3, name: "Swimming Pool", created_at: "2015-05-09 11:22:19", updated_at: "2015-05-09 16:09:41">, #<Facility id: 4, name: "Internet", created_at: "2015-05-09 11:24:02", updated_at: "2015-05-09 16:12:31">, #<Facility id: 5, name: "Dryer", created_at: "2015-05-09 15:55:36", updated_at: "2015-05-09 16:12:54">]>
我的代码有什么问题?有没有我错过的配置? 谢谢。
如果需要更多详细信息
#获取/facilities/1
facilities
和properties
是多对多的关系,我生成一个jointable
class CreateJoinTablePropertiesFacilities < ActiveRecord::Migration
def change
create_join_table :properties, :facilities do |t|
t.index [:property_id, :facility_id]
t.index [:facility_id, :property_id]
end
end
end
这是我的 facility.rb
class Facility < ActiveRecord::Base
has_and_belongs_to_many :property, join_table: :facilites_properties
end
这是我的 routes.rb
Rails.application.routes.draw do
namespace :admin do
resources :facilities
end
namespace :api do
namespace :v1 do
resources :properties
resources :facilities, only: [:index, :show]
resources :users do
member do
post 'list', :to => "users#list_property"
end
end
这是我的 app/controllers/api/v1/facilities_controller
class Api::V1::FacilitiesController < ApplicationController
before_action :set_facility, only: [:show, :edit, :update, :destroy]
# before_action :authenticate
# GET /facilities
# GET /facilities.json
def index
@facilities = Facility.all
render json: @facilities
end
# GET /facilities/1
# GET /facilities/1.json
def show
render json: @facility
end
private
# Use callbacks to share common setup or constraints between actions.
def set_facility
@facility = Facility.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def facility_params
params.require(:facility).permit(:name)
end
end
这是app/controllers/admin/facilities_controller
class Admin::FacilitiesController < ApplicationController
before_action :set_facility, only: [:show, :edit, :update, :destroy]
# before_action :authenticate
# GET /facilities
# GET /facilities.json
def index
@facilities = Facility.all
end
# GET /facilities/1
# GET /facilities/1.json
def show
end
# GET /facilities/new
def new
@facility = Facility.new
end
# GET /facilities/1/edit
def edit
end
# POST /facilities
# POST /facilities.json
def create
@facility = Facility.new(facility_params)
respond_to do |format|
if @facility.save
format.html { redirect_to admin_facilities_path, notice: 'Facility was successfully created.' }
format.json { render :show, status: :created, location: @facility }
else
format.html { render :new }
format.json { render json: @facility.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /facilities/1
# PATCH/PUT /facilities/1.json
def update
respond_to do |format|
if @facility.update(facility_params)
format.html { redirect_to admin_facilities_path, notice: 'Facility was successfully updated.' }
format.json { render :show, status: :ok, location: @facility }
else
format.html { render :edit }
format.json { render json: @facility.errors, status: :unprocessable_entity }
end
end
end
# DELETE /facilities/1
# DELETE /facilities/1.json
def destroy
@facility.destroy
respond_to do |format|
format.html { redirect_to facilities_url, notice: 'Facility was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_facility
@facility = Facility.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def facility_params
params.require(:facility).permit(:name)
end
end
我才意识到我忘记生成序列化程序了。由于我使用 gem Active Model Serializers,这就是我生成序列化程序所做的。
rails g serializer facility
堆栈跟踪显示请求的处理方式为 HTML。
Processing by Admin::FacilitiesController#index as HTML
当您尝试呈现 JSON 时,您可以指定控制器响应 json
module Api
module V1
class FacilitiesController < ApplicationController
respond_to :json
end
end
end
或者您可以将 json 指定为 routes.rb 文件中的默认响应
namespace :api, defaults: { format: 'json' } do
# your api json routes...
end
希望其中任何一个对您有所帮助。