2013-03-17 26 views
11

Rails 4增加了一個異常ActionDispatch :: ParamsParser :: ParseError異常,但由於它在中間件堆棧中出現,它不能在正常情況下獲救控制器環境。在json API應用程序中,我想用標準錯誤格式進行響應。如何在Rails 4中從ActionDispatch :: ParamsParser :: ParseError救援

gist顯示插入中間件攔截和響應的策略。根據這一模式,我有:

application.rb中:

module Traphos 
    class Application < Rails::Application 
    .... 
    config.middleware.insert_before ActionDispatch::ParamsParser, "JSONParseError" 
end 
end 

而中間件是:

class JSONParseError 
    def initialize(app) 
    @app = app 
    end 

    def call(env) 
    begin 
     @app.call(env) 
    rescue ActionDispatch::ParamsParser::ParseError => e 
     [422, {}, ['Parse Error']] 
    end 
    end 
end 

如果我跑我的測試,而不中間件我得到(SPEC) :

Failures: 

    1) Photo update attributes with non-parseable json 
    Failure/Error: patch update_url, {:description => description}, "CONTENT_TYPE" => content_type, "HTTP_ACCEPT" => accepts, "HTTP_AUTHORIZATION" => @auth 
    ActionDispatch::ParamsParser::ParseError: 
     399: unexpected token at 'description=Test+New+Description]' 

這正是我所期望的(ParseError,我不能rescue_from)。

與唯一的變化

我們在上面的中間件添加:

2) Photo update attributes with non-parseable json 
    Failure/Error: response.status.should eql(422) 

     expected: 422 
      got: 200 

和日誌顯示正在執行的標準控制器動作並返回正常應答(儘管因爲它沒有收到任何參數它沒有更新任何東西)。

我的問題:

  1. 如何從ParseError營救,並返回一個自定義的響應。感覺像我在正確的軌道上,但不是那裏。

  2. 我不能解決爲什麼,當異常被提出和救出時,控制器動作仍在繼續。

幫助非常感謝,--Kip

+0

有沒有其他一些中間件改變返回狀態?你有沒有用pry或其他方法進行調試? – phoet 2013-03-17 12:06:53

回答

6

原來,進一步向上中間件棧,ActionDispatch :: ShowExceptions可以與例外應用配置。

module Traphos 
    class Application < Rails::Application 
    # For the exceptions app 
    require "#{config.root}/lib/exceptions/public_exceptions" 
    config.exceptions_app = Traphos::PublicExceptions.new(Rails.public_path) 
    end 
end 

主要基於Rails的提供了一個我現在用:

module Traphos 
    class PublicExceptions 
    attr_accessor :public_path 

    def initialize(public_path) 
     @public_path = public_path 
    end 

    def call(env) 
     exception = env["action_dispatch.exception"] 
     status  = code_from_exception(env["PATH_INFO"][1..-1], exception) 
     request  = ActionDispatch::Request.new(env) 
     content_type = request.formats.first 
     body   = {:status => { :code => status, :exception => exception.class.name, :message => exception.message }} 
     render(status, content_type, body) 
    end 

    private 

    def render(status, content_type, body) 
     format = content_type && "to_#{content_type.to_sym}" 
     if format && body.respond_to?(format) 
     render_format(status, content_type, body.public_send(format)) 
     else 
     render_html(status) 
     end 
    end 

    def render_format(status, content_type, body) 
     [status, {'Content-Type' => "#{content_type}; charset=#{ActionDispatch::Response.default_charset}", 
       'Content-Length' => body.bytesize.to_s}, [body]] 
    end 

    def render_html(status) 
     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) 
     render_format(status, 'text/html', File.read(path)) 
     else 
     [404, { "X-Cascade" => "pass" }, []] 
     end 
    end 

    def code_from_exception(status, exception) 
     case exception 
     when ActionDispatch::ParamsParser::ParseError 
     "422" 
     else 
     status 
     end 
    end 
    end 
end 

要在測試環境中使用它需要設置配置變量(否則你得到標準的例外在開發和測試處理) 。因此,爲了測試我已經(編輯只是有關鍵部位):

describe Photo, :type => :api do 
    context 'update' do 
    it 'attributes with non-parseable json' do 

     Rails.application.config.consider_all_requests_local = false 
     Rails.application.config.action_dispatch.show_exceptions = true 

     patch update_url, {:description => description} 
     response.status.should eql(422) 
     result = JSON.parse(response.body) 
     result['status']['exception'].should match(/ParseError/) 

     Rails.application.config.consider_all_requests_local = true 
     Rails.application.config.action_dispatch.show_exceptions = false 
    end 
    end 
end 

因爲我需要在一個公共的API的方式執行,是適應於任何其他異常可能我選擇定製。

0

本文(也來自2013)thoughtbot也涵蓋此主題。他們把他們的迴應放在這個中間件服務裏面,只有當你要求json

if env['HTTP_ACCEPT'] =~ /application\/json/ 
    error_output = "There was a problem in the JSON you submitted: #{error}" 
    return [ 
     400, { "Content-Type" => "application/json" }, 
     [ { status: 400, error: error_output }.to_json ] 
    ] 
else 
raise error 
end