【问题标题】:Rails 4 - Respond only to JSON and not HTMLRails 4 - 只响应 JSON 而不是 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 错误。

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

【问题讨论】:

  • 我确实看到了,但我想知道为什么它不适用于 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/…

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


【解决方案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
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2018-12-19
              • 1970-01-01
              • 1970-01-01
              • 2016-05-30
              • 2016-09-09
              • 1970-01-01
              • 1970-01-01
              • 2018-12-17
              相关资源
              最近更新 更多