需要在Rails中返回JSON格式的404错误


77

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

Answers:


114

一位朋友向我指出了一种优雅的解决方案,该解决方案不仅可以处理404错误,还可以处理500个错误。实际上,它可以处理所有错误。关键在于,每个错误都会生成一个异常,该异常会向上传播到整个机架中间件,直到它们中的一个被处理为止。如果您想了解更多信息,可以观看此出色的截屏视频。Rails拥有自己的异常处理程序,但是您可以通过较少记录的exceptions_appconfig选项覆盖它们。现在,您可以编写自己的中间件,也可以将错误路由回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

4
另外,不要忘记将状态代码添加到渲染器。否则,您的客户端/浏览器将不会知道它是404/500。渲染:text =>“ 404 not found”,:status =>:not_found
arnab

1
我要说的是render_to块在渲染功能中更通用:format.json {渲染json:{错误:“未找到”} .to_json,状态:404} format.html {渲染文本:“ 404未找到”,状态:404}结束
Greg Funtusov 2013年

1
我已经为我的API控制器命名空间,并抛出一个异常,该异常具有一个类似ApiException的基类,然后可以在该基名称空间的Api控制器中对其进行抢救,其中可以以适当的状态返回JSON友好错误,如上。
理查德·霍利斯

@RichardHollis我的API控制器也具有命名空间,但是我能实现你的所作所为吗?
darksky

2
在采用上述建议的解决方案之前,请先查看此答案:stackoverflow.com/a/29292738/2859525。仅在祖先控制器中捕获404并使用回调进行简单的JSON错误响应就容易得多。
Todd

14

我喜欢创建一个单独的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

3
这似乎仅适用于记录。您将如何处理此案api/non_existant_route
Sebastialonso

1
该行rescue_from ActiveRecord::RecordNotFound, with: not_found必须是,with: :not_found但只能是一个字符编辑:P
错误

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版本和所需的确切行为而异。用Google搜索很容易找到。


2
感谢您的想法,但是我使用的是Rails 3.2.2,其中异常处理发生了变化。这将不再起作用。
iblue 2012年

@iblue在Rails 3.2+中可以正常工作,是更好的解决方案。有关更多信息,请参阅rescue_from 文档
Intelekshual

5

尝试放在您的末尾routes.rb

match '*foo', :format => true, :constraints => {:format => :json}, :to => lambda {|env| [404, {}, ['{"error": "not_found"}']] }

1
对于任何人读这篇文章,match将在Rails.application.routes文件现在需要你指定HTTP方法,通过via:参数。当您尝试使用上面的行时,这也会显示为错误。
Sameers
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.