株式会社ユーザベースの技術チームブログです。Ringアプリケーションで例外をいい感じにハンドリングする方法(Ductでの解説も含む)2019-10-07Ringアプリケーションで例外をいい感じにハンドリングする方法(Ductでの解説も含む)Tweetこんにちは!こんにちは!SPEEDA開発チームのあやぴーです。社内のClojureを使ったAPIにおいて、「例外をうまくハンドリングしたいんだけど...」という話が出てきたので、今回はRingアプリケーションにおける例外のハンドリング方法について解説します。昨今Ductを使ってアプリケーションを作る機会が増えているので、それについては最後の方に解説をします。具体的なコードはGitHubにあるので、参考にしてみてください。https://github.com/ayato-p/exception-handlingこの記事で解説する具体的な内容については以下の通り例外のハンドリングにRingミドルウェアを使う例外によってレスポンスを変更するDuctアプリケーションへの適用方法まとめ例外のハンドリングにRingミドルウェアを使うRingアプリケーションの特にRingハンドラー(以下、ハンドラー)部分で例外が出る場合について考えてみます。簡単のために以下のように、例外を投げる処理do-somethingを実行するハンドラーthrow-exception-handlerがあることにします。res)))本来であればdo-somethingが適当な値を返して、それをハンドラーのレスポンスとして返すところですが、この例ではdo-somethingが例外を起こすため、適切なレスポンスを返すことができません(Ringアダプターの実装にもよりますが、多くの場合は500エラーが返ると思います)。このハンドラーの挙動は以下のテストで確認することができます。data))))))このようにハンドラーの中で例外が発生する場合、各ハンドラーやその手前のレイヤーで適切にハンドリングして、例外をハンドラーの外側に伝播させないというのも場合によってはやると思います。予期しない例外や一般的な例外などに対してすべてのハンドラーで対応するのはあまりにも面倒です。いった例外をハンドルするための、いい感じの機能が欲しいところです。Javaの場合、JAX-RSのExceptionMapperあたりが今回欲しい機能に該当しそうです。Ringには残念ながら同じようなものはありません。そのため、少しだけ考えてみることにします。上記のテストコードがヒントになりそうです。上のコードのようにthrow-exception-handlerにリクエストマップを渡すようなコードをtry~catchで囲んで、例外を吐き出したときのみ例外のレスポンスマップを返すように書くことを考えれないでしょうか。500))))このようにハンドラーを実行して、その結果次第でレスポンスに影響を与える方法を僕らは既に知っているはずです。Ringミドルウェア(以下、ミドルウェア)です。次のようなミドルウェアwrap-exception-handlerを考えてみます。500))))))これはハンドラーを受け取って、新しいハンドラーを返します。新しいハンドラーは元のハンドラーに対して、自身が受け取ったリクエストを適用するだけで基本的には何もしませんが、例外が投げられたときにそれをキャッチして500のステータスコードを返すようにしています。wrap-exception-handlerは以下のテストコードで機能していることを確認することができます。req))))))例外によってレスポンスを変更する例外をハンドルすることができたので、今度は例外の種類によって返すレスポンスを変化させてみます。想像がついていると思いますが、try~catchで掴む例外を複数用意すれば簡単にレスポンスを変えることができます。以下ではIllegalArgumentExceptionのときに500、clojure.lang.ExceptionInfoのときはex-dataの:exception/typeの値によってステータスコードを変えています。Exception:"と流すようにしています。req))))))))こうなるようにwrap-exception-handlerを実装すると、以下のようになります。not-found-response)を作りました。実際に例外をふりわけるところはExceptionToResponseというプロトコルに任せて、それぞれの例外ごとに実装をできるようにしています。これによってwrap-exception-handlerでやっていることは非常に明快になりました。このようにRingミドルウェアを応用することで、例外ごとに任意のレスポンスを返すことができました。上記のwrap-exception-handlerを適用するDuctアプリケーションに適用するのは非常に簡単です。プロジェクトを用意するところから。次のコマンドを使ってDuctプロジェクトの雛形を作ります。+ataraxy今回はAtaraxyというデータでルーティングを記述できるライブラリを利用します。これはDuctで既にモジュール化されているため、簡単に使い始めることができます。次に先程のwrap-exception-handlerを使って、Ductから使えるコンポーネントを用意します。wrap-exception-handler)wrap-exception-handlerの実装については、既に説明したものをそのまま利用します。ミドルウェアのコンポーネント:demo.middleware/exception-handlerでは、単にwrap-exception-handlerを返すようにします。次に、このミドルウェアを適用するには、config.ednに以下のような記述をします。大事なところは:duct.module/ataraxyのキーに対応するマップです。module.ataraxyのREADMEに譲りますが、Ataraxyのシンタックスでいうresult部に対してメタ情報として任意のミドルウェアを指定することで先程のミドルウェアのコンポーネントを適用することができます。私たちの開発チ...2019-09-17Rustで非同期リクエストでハマったことこんにちは!SPEEDAプロダクト開発チームの成です。1.10.x時代の新しいデバッグツールこんにちは!こんにちは!SPEEDA開発チーム(通称PDT)に所属して...2018-04-03はじめてのDuctSPEEDA開発の中村です。Kubernetesのネットワーク...こんにちは。SPEEDA開発チームの鈴木です。»プロフィール『経済情報で、世界を変える』をミッションに、企業・業界情報プラットフォーム「SPEEDA」やソーシャル経済ニュース「NewsPicks」、ABMを強力にサポートする「FORCAS」、スタートアップの情報収集を圧倒的効率化「entrepedia」などを提供する株式会社ユーザベースの技術チームブログです。多国通貨」をハンズオンしたら快適で楽しかったGo言語でオブジェクト指向プログラミングの基本(型編)月別アーカイブ検索@Uzabase_Techさんのツイートスターをつけました引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。