RoutingErrorやRack::QueryParser::InvalidParameterErrorが発生した場合にはRackミドルウェアベースでエラーハンドリングされます。今回はこのエラーハンドリングの処理についてコードリーディングしていきます。Railsのバージョンは5.1.5です。

ミドルウェアはこんな感じの並びで ActionDispatch::DebugExceptions, ActionDispatch::ShowExceptionsのRackミドルウェアがエラーハンドリングします。

ActionDispatch::DebugExceptionsは以下のように定義されています。routingが見つからない場合、X-Cascadeヘッダにpassがセットされるため、ActionController::RoutingErrorがraiseされます。

ActionDispatch#show_exceptions?はconfig.action_dispatch.show_exceptionsがfalseの場合のみfalseでそれ以外はtrueになります。デフォルトでtest環境以外はtrueがセットされるため、show_exceptions?はtrueになります。

action_dispatch.show_detailed_exceptionsはデフォルトでconfig.consider_all_requests_localの値が入ります。

DebugExceptionsでエラーがraiseされた場合、ActionDispatch::ShowExceptionsでエラーハンドリングします。ActionDispatch::Request.show_exceptions?がtrueの場合はrender_exceptionが呼び出されます。

#render_exceptionではrequestのpath_infoを”/#{status}”に書き換えて@exceptions_appの#callを呼び出します。

@exceptions_appはconfig.exceptions_appが未設定の場合はActionDispatch::PublicExceptionsが設定されます。

ActionDispatch::PublicExceptionsはパスからステータスコードを取得し#renderでステータスコードに応じたpublicの静的ファイルをレンダリングします。

補足

routingで定義されていないパスを指定するとRoutingErrorが発生します。ActionDispatch::Journey::Router#serve内でルーティングが見つからなければ404のステータスコードとともにレスポンスヘッダにX-Cascade: “pass”をセットしています。

クエリパラメータにURIデコード不能な文字列(こういうやつ→%? )を入れるとエラーになります。Rack::QueryParser::InvalidParameterErrorが発生する場所はActionController::Instrumentation.process_actionのActionDispatch::Request#filetered_parametersを呼び出しているところです。

デコードできない文字列は最終的にRack::QueryParser#parse_nested_queryのunescapeのところでエラーになります。

ちなみにQueryParserのエラーの方はBetterErrorsの前方でエラーをraiseしてくれるのでBetterErrorsで補足可能ですが、RoutingErrorはraiseではなくX-Cascade = “pass”でレスポンスヘッダを伝搬させてBetterErrorsの後方のミドルウェア(DebugExceptions)でraiseしているのでBetterErrorsで補足できません。