[解決済み] ASP.NET Core Web APIの例外処理
質問
長年、通常のASP.NET Web APIを使用してきた後、新しいREST APIプロジェクトにASP.NET Coreを使用しています。私はASP.NET Core Web APIで例外を処理する良い方法を見いだすことができません。私は例外処理フィルタ/属性を実装しようとしました。
public class ErrorHandlingFilter : ExceptionFilterAttribute
{
public override void OnException(ExceptionContext context)
{
HandleExceptionAsync(context);
context.ExceptionHandled = true;
}
private static void HandleExceptionAsync(ExceptionContext context)
{
var exception = context.Exception;
if (exception is MyNotFoundException)
SetExceptionResult(context, exception, HttpStatusCode.NotFound);
else if (exception is MyUnauthorizedException)
SetExceptionResult(context, exception, HttpStatusCode.Unauthorized);
else if (exception is MyException)
SetExceptionResult(context, exception, HttpStatusCode.BadRequest);
else
SetExceptionResult(context, exception, HttpStatusCode.InternalServerError);
}
private static void SetExceptionResult(
ExceptionContext context,
Exception exception,
HttpStatusCode code)
{
context.Result = new JsonResult(new ApiResponse(exception))
{
StatusCode = (int)code
};
}
}
そして、これが私のStartupフィルター登録です。
services.AddMvc(options =>
{
options.Filters.Add(new AuthorizationFilter());
options.Filters.Add(new ErrorHandlingFilter());
});
私が抱えていた問題は、例外が発生したときに私の
AuthorizationFilter
で処理されない。
ErrorHandlingFilter
. 昔のASP.NET Web APIで動作したように、そこで捕捉されると期待していたのですが。
では、アプリケーションの例外とアクション・フィルタの例外をすべてキャッチするにはどうすればよいのでしょうか。
解決方法は?
クイック&イージーな解決策。
ASP.NETルーティングの前にこのミドルウェアをミドルウェアの登録に追加するだけです。
app.UseExceptionHandler(c => c.Run(async context =>
{
var exception = context.Features
.Get<IExceptionHandlerPathFeature>()
.Error;
var response = new { error = exception.Message };
await context.Response.WriteAsJsonAsync(response);
}));
app.UseMvc(); // or .UseRouting() or .UseEndpoints()
ロギングなどのためにDependency Injectionを有効にします。
ステップ1. スタートアップで、例外処理ルートを登録します。
// It should be one of your very first registrations
app.UseExceptionHandler("/error"); // Add this
app.UseEndpoints(endpoints => endpoints.MapControllers());
ステップ2. すべての例外を処理し、エラーレスポンスを生成するコントローラを作成します。
[AllowAnonymous]
[ApiExplorerSettings(IgnoreApi = true)]
public class ErrorsController : ControllerBase
{
[Route("error")]
public MyErrorResponse Error()
{
var context = HttpContext.Features.Get<IExceptionHandlerFeature>();
var exception = context.Error; // Your exception
var code = 500; // Internal Server Error by default
if (exception is MyNotFoundException) code = 404; // Not Found
else if (exception is MyUnauthException) code = 401; // Unauthorized
else if (exception is MyException) code = 400; // Bad Request
Response.StatusCode = code; // You can use HttpStatusCode enum instead
return new MyErrorResponse(exception); // Your error model
}
}
いくつかの重要な注意と観察。
- 以下のことが可能です。 依存関係を注入する を Controller のコンストラクタに追加します。
-
[ApiExplorerSettings(IgnoreApi = true)]
が必要です。さもないと、スワッシュバックルの威厳が壊れるかもしれません -
もう一度
app.UseExceptionHandler("/error");
は、Startupの中で非常に上位の登録でなければなりません。Configure(...)
メソッドを使用します。メソッドの一番上に配置するのが無難でしょう。 -
のパスは
app.UseExceptionHandler("/error")
とコントローラ内の[Route("error")]
は、例外ハンドラミドルウェアからリダイレクトされた例外をコントローラが処理できるようにするために、同じであるべきです。
以下は リンク をクリックすると、マイクロソフトの公式ドキュメントが表示されます。
レスポンスモデルのアイデア
独自のレスポンスモデルや例外処理を実装してください。 この例はあくまでも出発点です。どのサービスも、独自の方法で例外を処理する必要があるでしょう。このアプローチでは、例外処理を柔軟に制御し、 サービスから適切なレスポンスを返すことができます。
エラーレスポンスモデルの例(参考までに)
public class MyErrorResponse
{
public string Type { get; set; }
public string Message { get; set; }
public string StackTrace { get; set; }
public MyErrorResponse(Exception ex)
{
Type = ex.GetType().Name;
Message = ex.Message;
StackTrace = ex.ToString();
}
}
よりシンプルなサービスでは、次のようなhttpステータスコード例外を実装するとよいでしょう。
public class HttpStatusException : Exception
{
public HttpStatusCode Status { get; private set; }
public HttpStatusException(HttpStatusCode status, string msg) : base(msg)
{
Status = status;
}
}
このようにどこからでも投げることができる。
throw new HttpStatusCodeException(HttpStatusCode.NotFound, "User not found");
そうすると、処理するコードはこれだけに簡略化されます。
if (exception is HttpStatusException httpException)
{
code = (int) httpException.Status;
}
HttpContext.Features.Get<IExceptionHandlerFeature>()
WAT?
ASP.NET Coreの開発者は、Auth、MVC、Swaggerなどの機能の異なる側面を分離し、リクエスト処理パイプラインで順次実行するミドルウェアの概念を取り入れました。各ミドルウェアはリクエストコンテキストにアクセスでき、必要に応じてレスポンスに書き込むことができます。例外処理をMVCの外に持ち出すことは、MVC以外のミドルウェアからのエラーをMVCの例外と同じように処理することが重要な場合、理にかなっています(実際のアプリケーションでは非常によくあることだと思います)。例外処理ミドルウェアはMVCの一部ではないので、MVC自身は何も知りませんし、逆に例外処理ミドルウェアは例外がどこから来たのか、もちろんリクエスト実行のパイプのどこかで発生したことは知っていますが、本当のところは知りません。しかし、両者は互いに接続されている必要があります。そこで、例外がどこにも発生しない場合は、例外処理ミドルウェアが例外をキャッチして、パイプラインに登録されているルートを再実行します。このように、例外処理を一貫してMVCに戻すことができます。 コンテントネゴシエーション などのミドルウェアが必要です。例外そのものは、共通のミドルウェアのコンテキストから抽出されます。見た目はおかしいですが、これで仕事は完了です :) 。
関連
-
[解決済み】"出力タイプがクラスライブラリのプロジェクトは直接起動できない"
-
[解決済み] メンバー '<メンバー名>' にインスタンス参照でアクセスできない
-
[解決済み】SmtpException: トランスポート接続からデータを読み取れません:net_io_connectionclosed
-
[解決済み] Pythonで例外を手動で発生(スロー)させる
-
[解決済み] JUnit 4のテストで、ある例外が投げられたことをどのように断言しますか?
-
[解決済み] Pythonで例外を表示するには?
-
[解決済み] Pythonの関数が例外を投げるかどうかをテストするにはどうすればよいですか?
-
[解決済み] ASP.NET CoreでカスタムのAuthorizeAttributeを作成する方法は?
-
[解決済み】プログラムを停止/終了させることなく、完全な例外トレースバックをキャッチして表示する方法は?
-
[解決済み】ASP.NET Core RC2 Web ApiからHTTP 500を返すには?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】文字列が有効な DateTime " format dd/MM/yyyy " として認識されなかった。
-
[解決済み】Excel "外部テーブルが期待された形式ではありません。"
-
[解決済み】プロジェクトビルド時のエラー。エディタでスクリプトにコンパイルエラーがあるため、Playerのビルドにエラーが発生する
-
[解決済み】非静的メソッドはターゲットを必要とする
-
[解決済み] [Solved] 不正な文字列値: '\xEFxBFxBD' for column
-
[解決済み】Visual studio 2019がデバッグ時にフリーズする件
-
[解決済み】ランダムなブーリアンを生成する最速の方法
-
[解決済み] [Solved] .NETでスレッドの終了を待つには?
-
[解決済み】WebResource.axdとは何ですか?
-
[解決済み】データが存在しないのに読み込もうとする試みが無効である