可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I'm trying to build an API in rails 4, and am having an issue where rails returns a 500 error instead of a 406 when using respond_to :json
and trying to access the html version.
Here's an example controller demonstrating the problem:
class PostsController < ApplicationController
respond_to :json
def index
@posts = Post.all
end
end
I also have a jbuilder view for index
that works when accessing via JSON. If I try accessing the route without the JSON extension, It attempts to load the HTML template (which doesn't exist) and returns a 500 error, instead of just rendering JSON or returning a 406 error.
What could be causing this? Cheers for any help.
回答1:
I believe there are 2 parts here:
1) json only requests in rails
2) json only responses in rails
1) Configure your application controller to ensure json requests only
# app/controller/application_controller.rb
before_action :ensure_json_request
def ensure_json_request
return if request.format == :json
render :nothing => true, :status => 406
end
2) Configure your Rails API routes to ensure json responses only
# config/routes.rb
MyApp::Application.routes.draw do
namespace :api, constraints: { format: 'json' } do
namespace :v1 do
resources :posts
end
end
end
回答2:
To avoid loading the non-existent HTML template, set the default resource type as JSON in config/routes.rb:
resources :posts, :defaults => { :format => :json }
回答3:
In Rails 4, you need to pass a lambda to enforce the constraint on a route.
Unfortunately, this will NOT work because it will still try to serve up (or attempt to serve up) an html template since the format is an optional parameter:
resources :posts, constraints: { format: 'json' }
This DOES work (uses the lambda):
resources :posts, constraints: lambda { |req| req.format == :json }
See the second (final) note in this section of the Rails guide.
回答4:
As you are using a before_filter, you will have a 406 Not Acceptable if a request for a format is made which is not defined.
Example:
class SomeController < ApplicationController
respond_to :json
def show
@record = Record.find params[:id]
respond_with @record
end
end
The other way would be to add a before_filter to check for the format and react accordingly.
Example:
class ApplicationController < ActionController::Base
before_filter :check_format
def check_format
render :nothing => true, :status => 406 unless params[:format] == 'json'
end
end
But i think, you can just do it:
respond_to do |format|
format.json { render :json => @posts }
end
Further informations:
http://guides.rubyonrails.org/layouts_and_rendering.html
回答5:
You can try this, as I was also facing this issue and now it is solved by using this solution.
class PostsController < ApplicationController
respond_to :json
def index
@posts = Post.all
render json: @posts
end
end
回答6:
You can set it by having a before filter that sets the request to JSON explicitly.
request.format = :json
回答7:
constraints
was not working for POST requests and then I tried defaults
it works for all.
namespace :api, :defaults => { :format => 'json' } do
namespace :v1 do
resources :users do
collection do
get 'profile'
end
end
post 'signup' => 'users#create'
post 'login' => 'user_sessions#create'
end
end
回答8:
when you try a responses in json is beacuse you only need some properties i used this
@my_model=Model.select(:attributeN, :attributeN......, attributeN)
respond_to do |format|
format.json {
render json: @my_model
}
end
回答9:
I would suggest you to try gem 'active_model_serializers'
. Its really awesome and keeps clean.
ApplicationController:
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception, if: Proc.new { |c| c.request.format != 'application/json' }
protect_from_forgery with: :null_session, if: Proc.new { |c| c.request.format == 'application/json' }
end
Routes:
namespace :api, defaults: { format: :json } do
resource :posts
end
Posts Controller:
def index
render json: Post.all
end