Responderにはエラーハンドリング機能がない?

はじめに

今回はResponderを扱っていて、エラーハンドリングできなくね?って悩んで試行錯誤したので、それについて書こうと思います。

先に結論を言っておくと、やはり簡単にはできなさそうということがわかりました。
調べたり試行錯誤した結果を書くだけで、具体的な解決策は特に提示しないことをご了承ください。

@app.errorhanderやりたい

@app.errorhander is 何

@app.errorhanderとは何かという話ですが、エラーハンドリングを設定できる機能です。(そのまま)

Flaskの場合、特定のエラーコードのabortや例外が発生したとき、errorhandlerを設定することで、適切な処理を定義することができます。

@app.errorhandler(404)
def page_not_found(error):
    return 'This page does not exist', 404

@app.errorhandler(DatabaseError)
def special_exception_handler(error):
    return 'Database connection failed', 500

デコレータを使うだけで簡単に実装でき、例外処理を一括で担ってくれるので、大変便利です。

Responderでは使えない!?

さて、そんな便利なエラーハンドラーですが、Responderでは似たような機能が使えるのでしょうか?
答えは、おそらくNOです。

デコレータの場合

ResponderはそもそもStarletteというASGIフレームワークを利用して作られています。
そんなStarletteにはデコレータを使ったエラーハンドリングの機能があります。

@app.exception_handler(404)
async def not_found(request, exc):
    return HTMLResponse(content=HTML_404_PAGE, status_code=exc.status_code)

www.starlette.io

ですが、ResponderではStarletteの存在が完全に隠されており、この@app.exception_handlerといった呼び方はできません。

ミドルウェアを使う場合①

デコレータは諦めるとして他に方法はないのでしょうか?

Responderにはadd_middlewareというメソッドが、StarletteにはServerErrorMiddlewareというミドルウェアが用意されています。
ServerErrorMiddlewareにはユーザが自由にエラーハンドラーを指定できるので、これらをうまく使えば実現できそうです。

しかし、ResponderのメインであるAPIクラスでは、__init__時にServerErrorMiddleware初期化しています
設定を上書きすることはできないので、この方法も使えません。

試しに簡単に確認するために、初期化箇所をコメントアウトした上で、エラーハンドラーを定義してadd_middlewareServerErrorMiddlewareを設定してみると無事エラーハンドリングできました。
大元のコードを変更して使うわけにもいかないので、この方法も無理そうです。

ミドルウェアを使う場合②

StarletteにはExceptionMiddlewareというミドルウェアも用意されています。
これを使って独自に設定してみます。

api.add_middleware(ExceptionMiddleware)
api.app.add_exception_handler(Exception, error_handler)

しかし、RuntimeError: Caught handled exception, but response already started.というエラーが出て正しく動作しません。

ギブアップ!

色々と試行錯誤してみましたが、一筋縄ではいかない気がします。
力が足りないのか、コードリーディングが足りないのか、そもそも不可能なのか…
ここでギブアップです。

みんな困ってる?

色々調べた後にたどり着いたのですが、ResponderのIssueにまさに同じ内容のものがあがっていました。

github.com

ここを見る限り、投稿者のVyeさんは同じように困っているようです。
そして、現時点では有益なコメントもついていないようです。*1
全体的に活発にコメントがなされているかと言われるとそうではないですが、「こうすればいいよ」といったコメントや「同じく困ってるんだよね」というコメントも無いので、よくわからない状況です。
何か更新があれば良いのですが。

おわりに

結局、解決策もなく、できませんでした!という話になってしまいました。
簡単にエラーハンドリングができない(かもしれない)というだけで、個別に対応していけばなんとかなるわけですが、やはりデコレータあたりでさくっと設定できてほしいところです。

今後もResponderの動向を見ていくので、Issueなどに更新があれば追記していこうと思います。

*1:ひとつはStarletteのapp.add_exception_handler()が使えるのではないかというコメントですが、これでは無理そうです