Ruby-on-rails 需要在 Rails 中返回 JSON 格式的 404 错误

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10253366/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 03:16:08  来源:igfitidea点击:

Need to return JSON-formatted 404 error in Rails

ruby-on-rails

提问by iblue

I am having a normal HTML frontend and a JSON API in my Rails App. Now, if someone calls /api/not_existent_method.jsonit returns the default HTML 404 page. Is there any way to change this to something like {"error": "not_found"}while leaving the original 404 page for the HTML frontend intact?

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

回答by iblue

A friend pointed me towards a elegant solution that does not only handle 404 but also 500 errors. In fact, it handles every error. The key is, that every error generates an exception that propagates upwards through the stack of rack middlewares until it is handled by one of them. If you are interested in learning more, you can watch this excellent screencast. Rails has it own handlers for exceptions, but you can override them by the less documented exceptions_appconfig option. Now, you can write your own middleware or you can route the error back into rails, like this:

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

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

Then you just have to match these routes in your config/routes.rb:

然后你只需要在你的config/routes.rb

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

And then you just create a controller for handling this.

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

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

One last thing to add: In the development environment, rails usally does not render the 404 or 500 pages but prints a backtrace instead. If you want to see your ErrorsControllerin action in development mode, then disable the backtrace stuff in your config/enviroments/development.rbfile.

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

config.consider_all_requests_local = false

回答by Greg Funtusov

I like to create a separate API controller that sets the format (json) and api-specific methods:

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

RSpec 测试:

  it 'should return 404' do
    get "/api/route/specific/to/your/app/", format: :json
    expect(response.status).to eq(404)
  end

回答by bioneuralnet

Sure, it will look something like this:

当然,它看起来像这样:

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

NotFoundExceptionis notthe real name of the exception. It will vary with the Rails version and the exact behavior you want. Pretty easy to find with a Google search.

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

回答by jdoe

Try to put at the endof your routes.rb:

尝试放在你的末尾routes.rb

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