79

我的 Rails 应用程序中有一个普通的 HTML 前端和一个 JSON API。现在,如果有人调用/api/not_existent_method.json它,它将返回默认的 HTML 404 页面。有什么方法可以将其更改为类似的内容{"error": "not_found"},同时保留 HTML 前端的原始 404 页面?

4

4 回答 4

117

一位朋友向我指出了一个优雅的解决方案,它不仅可以处理 404 错误,还可以处理 500 个错误。事实上,它处理每一个错误。关键是,每个错误都会产生一个异常,该异常会通过机架中间件堆栈向上传播,直到由其中一个处理。如果您有兴趣了解更多信息,可以观看此出色的截屏视频。Rails 有自己的异常处理程序,但您可以通过较少记录的exceptions_app配置选项覆盖它们。现在,您可以编写自己的中间件,也可以将错误路由回 Rails,如下所示:

# In your config/application.rb
config.exceptions_app = self.routes

然后你只需要在你的匹配这些路线config/routes.rb

get "/404" => "errors#not_found"
get "/500" => "errors#exception"

然后你只需创建一个控制器来处理它。

class ErrorsController < ActionController::Base
  def not_found
    if env["REQUEST_PATH"] =~ /^\/api/
      render :json => {:error => "not-found"}.to_json, :status => 404
    else
      render :text => "404 Not found", :status => 404 # You can render your own template here
    end
  end

  def exception
    if env["REQUEST_PATH"] =~ /^\/api/
      render :json => {:error => "internal-server-error"}.to_json, :status => 500
    else
      render :text => "500 Internal Server Error", :status => 500 # You can render your own template here
    end
  end
end

最后要补充一点:在开发环境中,rails 通常不会渲染 404 或 500 页,而是打印回溯。如果您想ErrorsController在开发模式下查看您的实际操作,请禁用config/enviroments/development.rb文件中的回溯内容。

config.consider_all_requests_local = false
于 2012-04-21T08:37:07.517 回答
17

我喜欢创建一个单独的 API 控制器来设置格式(json)和特定于 api 的方法:

class ApiController < ApplicationController
  respond_to :json

  rescue_from ActiveRecord::RecordNotFound, with: :not_found
  # Use Mongoid::Errors::DocumentNotFound with mongoid

  def not_found
    respond_with '{"error": "not_found"}', status: :not_found
  end
end

RSpec 测试:

  it 'should return 404' do
    get "/api/route/specific/to/your/app/", format: :json
    expect(response.status).to eq(404)
  end
于 2014-06-27T11:53:26.230 回答
11

当然,它看起来像这样:

class ApplicationController < ActionController::Base
  rescue_from NotFoundException, :with => :not_found
  ...

  def not_found
    respond_to do |format|
      format.html { render :file => File.join(Rails.root, 'public', '404.html') }
      format.json { render :text => '{"error": "not_found"}' }
    end
  end
end

NotFoundException不是真名的例外它会因 Rails 版本和您想要的确切行为而异。用谷歌搜索很容易找到。

于 2012-04-20T21:32:27.123 回答
6

尝试放在你的末尾routes.rb

match '*foo', :format => true, :constraints => {:format => :json}, :to => lambda {|env| [404, {}, ['{"error": "not_found"}']] }
于 2012-04-20T21:48:33.633 回答