Webエンジニア向けプログラミング解説動画をYouTubeで配信中!
▶ チャンネル登録はこちら

【ITニュース解説】Global Exception Handling in .NET Web API

2025年09月23日に「Dev.to」が公開したITニュース「Global Exception Handling in .NET Web API」について初心者にもわかりやすく解説しています。

作成日: 更新日:

ITニュース概要

.NET Web API開発でtry-catchを各所に書くと、コードが複雑になりエラー応答も不統一になる。グローバル例外処理をミドルウェアで実装すれば、エラーを一元管理し、常に統一されたエラー応答を返せる。コントローラーはすっきりし、ログも取りやすくなるため、効率的なAPI開発に欠かせない。

出典: Global Exception Handling in .NET Web API | Dev.to公開日:

ITニュース解説

Web APIを開発する際、開発者がよく陥りがちな問題の一つに、エラーの処理方法がある。通常、プログラムで予期せぬ問題(例外)が発生した際には、try-catchブロックを使ってその問題を捕捉し、適切に処理することが求められる。しかし、これをAPIの各機能(コントローラー内の各メソッド)で個別に記述していくと、コードが非常に読みにくく、散らかった印象になってしまう。さらに、エラーが発生した際にクライアント(APIを利用するアプリケーション)に返すメッセージの形式や内容が、機能ごとにバラバラになってしまうという問題も発生する。これは、APIを利用する側から見ると、非常に使いにくく、混乱のもととなる。

このような問題を解決するための強力な手法が、「グローバル例外ハンドリング」と呼ばれるものだ。これは、アプリケーション全体で発生するあらゆる未処理の例外を一箇所でまとめて処理する仕組みを指す。特に.NET Web APIでは、「ミドルウェア」という機能を使ってこれを実現できる。

なぜグローバル例外ハンドリングが必要なのだろうか。まず、エラー処理を一元化できる点が挙げられる。アプリケーションのどこで例外が発生しても、すべて同じ処理経路を通るようになるため、エラー管理が非常に効率的になる。次に、クライアントへの応答に一貫性を持たせられる。どんなエラーが発生しても、常に同じ形式(例えばJSON形式)で、構造化されたエラーメッセージを返すことができるため、クライアント側でのエラー処理も簡単になる。さらに、コントローラーのコードが非常にすっきりする。本来のビジネスロジックだけを記述すればよく、エラー処理のためのtry-catchブロックを繰り返して書く必要がなくなるからだ。そして、アプリケーション全体の未処理例外を容易にログに記録できるようになるため、問題の特定やデバッグが格段に楽になる。

具体的に、.NET Web APIでグローバル例外ハンドリングを実装する手順を見ていこう。

最初のステップは、例外処理のための「ミドルウェア」を作成することだ。ミドルウェアとは、HTTPリクエストがアプリケーションに到達してから応答がクライアントに返されるまでの間に、処理を挟み込むことができる機能のことである。ここではGlobalExceptionMiddlewareというクラスを作成する。このクラスはInvokeAsyncという非同期メソッドを持っており、このメソッドの中で実際の例外処理が行われる。

GlobalExceptionMiddlewareクラスには、_next_loggerという二つの重要な要素がある。_nextは、次のミドルウェア、または最終的なAPIエンドポイント(コントローラーのメソッドなど)を呼び出すためのデリゲート(処理の参照)だ。_loggerは、例外が発生した際にその情報をログに出力するために使われる。

InvokeAsyncメソッドの中では、try-catchブロックが使われている。tryブロックの中では、await _next(context);が呼び出されている。これは、このミドルウェアの後に続く本来のAPI処理を実行することを意味する。もしこの処理中に何らかの例外が発生した場合、その例外はcatch (Exception ex)ブロックで捕捉される。例外が捕捉されたら、まず_logger.LogErrorを使って、発生した例外と「未処理の例外が発生しました」というメッセージをログに出力する。これにより、後から問題の原因を調査する際に役立つ情報が残される。

その後、HandleExceptionAsyncという別のメソッドが呼び出される。このメソッドの目的は、クライアントに返すエラーレスポンスを生成することだ。HandleExceptionAsyncメソッドでは、まずcontext.Response.ContentType = "application/json";として、応答の形式がJSONであることを指定する。次に、context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;として、HTTPステータスコードを「500 Internal Server Error」に設定する。これは、サーバー側で予期せぬエラーが発生したことを示す標準的なコードである。

そして、クライアントに返すJSON形式の応答オブジェクトを作成する。このオブジェクトには、HTTPステータスコード(500)、一般的なエラーメッセージ("Internal Server Error. Please try again later.")、そしてオプションとして例外の詳細メッセージ(exception.Message)が含まれる。この詳細メッセージは開発時には有用だが、セキュリティ上の理由から本番環境では含めない方が良い場合もある。最後に、context.Response.WriteAsJsonAsync(response);を使って、作成したJSON応答をクライアントに書き出す。

次に、作成したミドルウェアをアプリケーションに登録するステップだ。これは、アプリケーションの起動時に実行されるProgram.csファイルで行う。var app = builder.Build();でアプリケーションが構築された後、app.UseMiddleware<GlobalExceptionMiddleware>();という一行を追加する。この記述により、GlobalExceptionMiddlewareがアプリケーションのHTTPリクエスト処理パイプラインに組み込まれる。これで、以降のすべてのリクエストで、APIの処理が始まる前にこのミドルウェアが介入し、例外が発生すれば適切に処理されるようになる。

ミドルウェアが正しく動作するかを確認するために、テストを行う。例えば、TestControllerというAPIコントローラーを作成し、その中に意図的に例外を発生させるメソッド(GetErrorなど)を実装する。このメソッドが呼び出された際に、throw new InvalidOperationException("This is a test exception.");のように例外をスローするようにする。

このGET /api/test/errorというエンドポイントにリクエストを送信すると、アプリケーション内で例外が発生する。しかし、この例外は先ほど登録したGlobalExceptionMiddlewareによって捕捉され、処理されるため、クライアントには標準的なJSON形式のエラーレスポンスが返されるはずだ。具体的には、HTTPステータスコードは500、メッセージは「Internal Server Error. Please try again later.」、そして詳細には「This is a test exception.」という、期待通りのJSON応答が得られる。

グローバル例外ハンドリングには、さらにいくつかのオプションの拡張が考えられる。例えば、環境に応じたメッセージの表示だ。開発環境では例外の詳細メッセージを表示してデバッグに役立てる一方で、本番環境ではセキュリティと情報漏洩防止のために詳細メッセージを非表示にするといった制御が可能になる。また、特定の種類の例外(例えばユーザーからの入力値が不正だった場合の検証エラー)に対して、異なるHTTPステータスコード(400 Bad Requestなど)やメッセージを返すように、カスタム例外タイプを定義して処理を分岐させることもできる。さらに、エラーが発生した際に一意の「相関ID」を含んだ構造化ログを出力することで、複数のログファイルを横断して特定のリクエストの処理追跡が容易になる。より高度な方法として、Problem Details (RFC 7807)という標準仕様に準拠したエラーレスポンスを返すことで、異なるクライアントやシステム間でのエラー処理の互換性を高めることもできる。

このように、グローバル例外ハンドリングは、Web APIのコードを簡潔に保ち、エラー応答の一貫性を確保し、保守性を向上させる上で非常に重要なパターンである。エラー管理を一元化することで、コントローラーから反復的なtry-catchブロックを排除し、ログ記録を容易にし、クライアントが常に予測可能な応答を受け取れるようにする。このパターンは、実運用に耐えうるすべての.NET Web APIにとって必須の機能と言える。

関連コンテンツ

関連IT用語