Rails 4 - 只响应 JSON 而不是 HTML

Posted

技术标签:

【中文标题】Rails 4 - 只响应 JSON 而不是 HTML【英文标题】:Rails 4 - Respond only to JSON and not HTML 【发布时间】:2014-03-25 13:46:06 【问题描述】:

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

这可能是什么原因造成的?为任何帮助干杯。

【问题讨论】:

可能重复:***.com/questions/14579774/… 我确实看到了,但我想知道为什么它不适用于 respond_to :json 设置 respond_to :jsonrespond_with @posts 在生产中正确响应 HTTP/1.1 406 Not Acceptable 给我。在开发中,您会收到“HTTP/1.1 500 Internal Server Error”和ActionController::UnknownFormat 对不起,我试图回答,但我的帖子被版主删除了,因为我还回答了另一个“可能重复”,即使这个更清楚的是 Rails 4。所以你去......在 Rails 4,你必须在路由中传递一个 lambda 来强制执行这样的约束:resources :posts, constraints: lambda |req| req.format == :json 您可以在 Rails 指南的第二个注释中看到更多信息:edgeguides.rubyonrails.org/… 【参考方案1】:

我相信这里有两个部分: 1) json requests 在 rails 2) json 仅 responses 在 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  

【讨论】:

你可以使用request.format.symbol == :json代替params[:format] == "json" || request.headers["Accept"] =~ /json/ 很好的电话。用request.format == :json 更新,因为symbol 不需要其他request format question 你可以使用head :not_acceptable而不是render :nothing =&gt; true, :status =&gt; 406【参考方案2】:

为避免加载不存在的 HTML 模板,请在 config/routes.rb 中将默认资源类型设置为 JSON:

resources :posts, :defaults =>  :format => :json 

【讨论】:

这不提供约束,这是问题所暗示的。这个想法是,如果没有指定 .json 格式,rails 将不会在路线上匹配并继续向下层级。【参考方案3】:

在 Rails 4 中,您需要传递一个 lambda 来强制对路由进行约束。

不幸的是,这不起作用,因为它仍然会尝试提供(或尝试提供)html模板,因为格式是可选参数:

resources :posts, constraints:  format: 'json' 

这确实有效(使用 lambda):

resources :posts, constraints: lambda  |req| req.format == :json 

请参阅this section of the Rails guide 中的第二条(最后一条)注释。

【讨论】:

这似乎不适用于scope,但defaults: format: 'json' 确实有效。【参考方案4】:

当您使用 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

【讨论】:

我遇到的问题是respond_to :json 仍然允许 html 响应通过。无论请求的格式如何,我都希望所有响应都是 JSON。 您是否在视图上创建了 json? params[:format] == 'json' 还不够。当请求使用Accept: application/json 标头发出时,params[:format] 为空,但 Rails 仍以 JSON 响应。【参考方案5】:

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

【讨论】:

【参考方案6】:

你可以试试这个,因为我也遇到过这个问题,现在用这个解决方案解决了。

class PostsController < ApplicationController
  respond_to :json

  def index
    @posts = Post.all
    render json: @posts
  end
end

【讨论】:

【参考方案7】:

您可以通过使用将请求显式设置为 JSON 的前置过滤器来设置它。

request.format = :json

【讨论】:

【参考方案8】:

当你在 json 中尝试响应是因为你只需要一些我使用的属性

@my_model=Model.select(:attributeN, :attributeN......, attributeN)
respond_to do |format|
  format.json 
    render json: @my_model
  
end

【讨论】:

【参考方案9】:

我建议你试试gem 'active_model_serializers'。它真的很棒,而且很干净。

应用控制器:

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

【讨论】:

【参考方案10】:

我有点晚了,但是关于你对this answer的评论:

我希望所有响应都是 JSON

最简单的解决方案是:

respond_to do |format|
  format.all  render :json => @posts 
end

【讨论】:

以上是关于Rails 4 - 只响应 JSON 而不是 HTML的主要内容,如果未能解决你的问题,请参考以下文章

Rails - 格式和渲染 - 它们是如何工作的?

Fabrik表单提交问题,获取JSON响应而不是感谢页面

如何最好地使用批量数据json在rails中渲染?

AFNetworking 将 text/html 读取为 html 而不是 JSON

为啥'javascript_pack_tag'只适用于'body'而不是html'head'(Rails 6和webpack)

Rails 3:在 Rails 中使用 JSON 响应 REST-ful 操作的正确方法是啥?