我正在尝试在rails 4中构建一个API,并且在使用respond_to :json
并尝试访问html版本时,rails遇到500错误而不是406错误的问题。
这是一个演示问题的示例控制器:
class PostsController < ApplicationController
respond_to :json
def index
@posts = Post.all
end
end
我还有index
的jbuilder视图,当通过JSON访问时可以正常工作。如果我尝试访问没有JSON扩展名的路由,它会尝试加载HTML模板(不存在)并返回500错误,而不是仅渲染JSON或返回406错误。
可能导致这种情况的原因是什么?欢呼任何帮助。
答案 0 :(得分:32)
我相信这里有两个部分:
1)json只有请求在rails中
2)json仅在rails
1)配置应用程序控制器以确保仅 json请求
# 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)配置Rails API路由以确保仅 json响应
# config/routes.rb
MyApp::Application.routes.draw do
namespace :api, constraints: { format: 'json' } do
namespace :v1 do
resources :posts
end
end
end
答案 1 :(得分:30)
为避免加载不存在的HTML模板,请在config / routes.rb中将默认资源类型设置为JSON:
resources :posts, :defaults => { :format => :json }
答案 2 :(得分:13)
在Rails 4中,您需要传递一个lambda来强制执行路由约束。
不幸的是,这不起作用,因为它仍然会尝试提供(或尝试提供)html模板,因为格式是可选参数:
resources :posts, constraints: { format: 'json' }
此DOES工作(使用lambda):
resources :posts, constraints: lambda { |req| req.format == :json }
请参阅this section of the Rails guide中的第二个(最终)注释。
答案 3 :(得分:5)
当您使用before_filter时,如果对格式的请求未定义,您将获得406 Not Acceptable。
示例:
class SomeController < ApplicationController
respond_to :json
def show
@record = Record.find params[:id]
respond_with @record
end
end
另一种方法是添加一个before_filter来检查格式并做出相应的反应。
示例:
class ApplicationController < ActionController::Base
before_filter :check_format
def check_format
render :nothing => true, :status => 406 unless params[:format] == 'json'
end
end
但我认为,你可以这样做:
respond_to do |format|
format.json { render :json => @posts }
end
进一步的信息: http://guides.rubyonrails.org/layouts_and_rendering.html
答案 4 :(得分:3)
constraints
无法处理POST请求,然后我尝试defaults
它适用于所有人。
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
答案 5 :(得分:2)
你可以试试这个,因为我也面临这个问题,现在通过使用这个解决方案解决了。
class PostsController < ApplicationController
respond_to :json
def index
@posts = Post.all
render json: @posts
end
end
答案 6 :(得分:1)
您可以通过使用before过滤器来设置它,以明确地将请求设置为JSON。
request.format = :json
答案 7 :(得分:0)
当你在json中尝试响应时,你只需要一些我用过的属性
@my_model=Model.select(:attributeN, :attributeN......, attributeN)
respond_to do |format|
format.json {
render json: @my_model
}
end
答案 8 :(得分:0)
我建议你试试gem 'active_model_serializers'
。它非常棒,保持干净。
<强>的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
路线:
namespace :api, defaults: { format: :json } do
resource :posts
end
帖子控制器:
def index
render json: Post.all
end
答案 9 :(得分:0)
我有点迟了,但是关于您对this answer的评论:
我希望所有响应均为JSON
最简单的解决方案是:
respond_to do |format|
format.all { render :json => @posts }
end