Rails用jbuilder显示错误的正确方法

ran*_*its 7 ruby json ruby-on-rails jbuilder

我希望在jbuilder视图中显示错误消息.例如,我可能有的一条路线可能是:

/foos/:id/bars

如果:id用户提交的内容不存在或无效,我希望能够在我的index.json.builder文件中相应地显示错误消息.

使用Rails,完成这项工作的最佳方法是什么?控制器可能具有以下内容:

def index
  @bar = Bar.where(:foo_id => params[:id])
end
Run Code Online (Sandbox Code Playgroud)

在这种情况下,params[:id]可能是nil,或该对象可能不存在.我不确定这里做的最好的事情是在控制器中处理它并显式渲染error.json.builder,或者在index.json.builder视图中处理它.这样做的正确方法是什么,如果它在index.json.builder,params[:id]可以在那里检查?我知道我可以看到反过来@bar.nil?但不确定吗?

Shk*_*rik 5

我会渲染 index.json.builder 或只是内联 json:error => 'not found' 并且不要忘记设置正确的 HTTP 状态::status => 404

所以结果可能如下所示:

render :json => { :error => 'not found' }, :status => 422 if @bar.nil?
Run Code Online (Sandbox Code Playgroud)


Gar*_*ver 5

我认为您的意思是显示,因为索引实际上是用于列表/集合。你应该去.first哪里,否则你只是有关系,对吗?然后,用于.first!引发错误,因为 Rails 的 Rack 中间件在 Rails 4 public_exceptions 中将以基本方式处理,例如

def show
  # need to do to_s on params value if affected by security issue CVE-2013-1854
  @bar = Bar.where(:foo_id => params[:id].to_s).first!
end
Run Code Online (Sandbox Code Playgroud)

您也可以使用@bar = Bar.find(params[:id]),但它已被弃用,并将在 Rails 4.1 中删除,之后您必须将其添加gem 'activerecord-deprecated_finders'到 Gemfile 中才能使用。

对于索引,您可能需要@bars = Bar.all. 如果由于某种原因您想要过滤并且不想限定范围等,那么您可以使用@bars = Bar.where(...).to_a或类似。

Rails 4:机架中的基本异常处理是自动的

只要查询引发错误,Rails 4 就应该能够返回错误的消息部分,任何支持的格式where to_(format)都可以在散列上调用(例如 json、xml 等)。

要了解原因,请查看 Rails 的 Rack public_exceptions中间件。

如果是 html,它将尝试从 Rails 的公共目录中读取相关文件以获取状态代码(例如500.html,服务器错误/HTTP 500)。

如果是其他格式,它将尝试对to_(the format)哈希执行:{ :status => status, :error => exception.message }。要查看这是如何工作的,请转到 Rails 的控制台:

$ rails c
...
1.9.3p392 :001 > {status: 500, error: "herro shraggy!"}.to_xml
 => "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<hash>\n  <status type=\"integer\">500</status>\n  <error>herro shraggy!</error>\n</hash>\n" 
1.9.3p392 :002 > {status: 500, error: "herro shraggy!"}.to_json
 => "{\"status\":500,\"error\":\"herro shraggy!\"}" 
Run Code Online (Sandbox Code Playgroud)

在中间件中,您将X-Cascade在代码中以及在 Rack 中与 Rails 异常处理相关的各个位置看到标头。根据这个答案X-Cascade标头设置为pass告诉 Rack 尝试其他路由来查找资源。

Rails 3.2.x:可以处理机架中的异常

在 Rails 3.2.x 中,to_(format)用于响应主体等的代码不在public_exceptions.rb 中。它只处理 html 格式。

也许您可以尝试通过补丁将旧中间件替换为新版本。

如果您希望 Rack 在没有补丁的情况下以更具体的方式处理您的错误,请参阅 José Valim 的文章“我最喜欢的 Rails 3.2 中的五个“隐藏”功能中的#3 。

在那和另一个答案中也提到,您可以使用config.exceptions_app = self.routes. 然后使用指向自定义控制器的路由,您可以像处理任何其他请求一样处理来自任何控制器的错误。请注意config.consider_all_requests_local = false您的config/environments/development.rb.

您不必使用路由来使用exceptions_app. 虽然它可能有点吓人,但它只是一个 proc/lambda,它接受一个散列并返回一个格式为: 的数组[http_status_code_number, {headers hash...}, ['the response body']]。例如,您应该能够在 Rails 3.2.x 配置中执行此操作,以使其处理类似 Rails 4.0 的错误(这是最新的 public_exceptions 中间件已折叠):

config.exceptions_app = lambda do |env|
  exception = env["action_dispatch.exception"]
  status = env["PATH_INFO"][1..-1]
  request = ActionDispatch::Request.new(env)
  content_type = request.formats.first
  body = { :status => status, :error => exception.message }
  format = content_type && "to_#{content_type.to_sym}"
  if format && body.respond_to?(format)
    formatted_body = body.public_send(format)
    [status, {'Content-Type' => "#{content_type}; charset=#{ActionDispatch::Response.default_charset}",
            'Content-Length' => body.bytesize.to_s}, [formatted_body]]
  else
    found = false
    path = "#{public_path}/#{status}.#{I18n.locale}.html" if I18n.locale
    path = "#{public_path}/#{status}.html" unless path && (found = File.exist?(path))

    if found || File.exist?(path)
      [status, {'Content-Type' => "text/html; charset=#{ActionDispatch::Response.default_charset}",
              'Content-Length' => body.bytesize.to_s}, [File.read(path)]]
    else
      [404, { "X-Cascade" => "pass" }, []]
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

注意:对于该处理的任何问题,故障安全实现在ActionDispatch::ShowExceptions 这里

Rails 3 和 4:处理 Rails 控制器中的一些异常

如果您希望在控制器本身中呈现错误,您可以执行以下操作:

def show
  respond_with @bar = Bar.where(:foo_id => params[:id].to_s).first!
rescue ActiveRecord::RecordNotFound => e
  respond_to do |format|
    format.json => { :error => e.message }, :status => 404
  end
end
Run Code Online (Sandbox Code Playgroud)

但是,您不需要引发错误。你也可以这样做:

def show
  @bar = Bar.where(:foo_id => params[:id].to_s).first
  if @bar
    respond_with @bar
  else
    respond_to do |format|
      format.json => { :error => "Couldn't find Bar with id=#{params[:id]}" }, :status => 404
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

您还可以使用rescue_from,例如在您的控制器或 ApplicationController 等中:

rescue_from ActiveRecord::RecordNotFound, with: :not_found

def not_found(exception)
  respond_to do |format|
    format.json => { :error => e.message }, :status => 404
  end
end
Run Code Online (Sandbox Code Playgroud)

或者:

rescue_from ActiveRecord::RecordNotFound do |exception|
  respond_to do |format|
    format.json => { :error => e.message }, :status => 404
  end
end
Run Code Online (Sandbox Code Playgroud)

虽然一些常见的错误可以在控制器中处理,但如果你的错误与丢失的路由等相关,格式为 json 等,则需要在 Rack 中间件中处理。