【问题标题】:Rails proper way to display error with jbuilderRails 使用 jbuilder 显示错误的正确方法
【发布时间】:2013-03-27 02:07:29
【问题描述】:

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

/foos/:id/bars

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

使用 Rails,最好的方法是什么?控制器可能具有以下内容:

def index
  @bar = Bar.where(:foo_id => params[:id])
end

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

【问题讨论】:

    标签: ruby-on-rails ruby json jbuilder


    【解决方案1】:

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

    所以结果可能如下所示:

    render :json => { :error => 'not found' }, :status => 422 if @bar.nil?
    

    【讨论】:

      【解决方案2】:

      我认为您的意思是显示,因为索引实际上是用于列表/集合的。你应该在哪里得到.first,否则你只是有关系,对吧?然后,使用.first! 引发错误,因为Rails 4 中的Rails 机架中间件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
      

      您也可以使用@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!\"}" 
      

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

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

      在 Rails 3.2.x 中,为响应正文等执行 to_(format) 的代码不在 public_exceptions.rb 中。它只处理html格式。

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

      如果您希望 Rack 在没有补丁的情况下以更具体的方式处理您的错误,请参阅 José Valim 的帖子中的 #3,“My five favorite “hidden” features in Rails 3.2”。

      正如another answer 也提到的那样,您可以使用config.exceptions_app = self.routes。然后使用指向自定义控制器的路由,您可以像处理任何其他请求一样处理来自任何控制器的错误。请注意config/environments/development.rb 中有关config.consider_all_requests_local = false 的部分。

      您不必使用路由来使用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
      

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

      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
      

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

      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
      

      您也可以使用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
      

      或:

      rescue_from ActiveRecord::RecordNotFound do |exception|
        respond_to do |format|
          format.json => { :error => e.message }, :status => 404
        end
      end
      

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-11-09
        • 2021-05-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-09-14
        相关资源
        最近更新 更多