Rails 4 - 仅响应JSON而不是HTML

时间:2014-02-24 03:52:42

标签: ruby-on-rails ruby-on-rails-4

我正在尝试在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错误。

可能导致这种情况的原因是什么?欢呼任何帮助。

10 个答案:

答案 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