次の方法で共有


ASP.NET Core API のエラーを処理する

これは、この記事の最新バージョンではありません。 現在のリリースについては、 この記事の .NET 10 バージョンを参照してください。

Warnung

このバージョンの ASP.NET Core はサポート対象から除外されました。 詳細については、 .NET および .NET Core サポート ポリシーを参照してください。 現在のリリースについては、この記事の .NET 9 バージョンを参照してください。

この記事では、ASP.NET Core API のエラーを処理する方法について説明します。 最小限の API のドキュメントが選択されています。 コントローラー ベースの API のドキュメントを表示するには、[ コントローラー ] タブを選択します。エラー処理ガイダンス Blazor については、「 ASP.NET Core Blazor アプリでのエラーの処理」を参照してください。

開発者例外ページ

[開発者例外] ページには、未処理の要求例外に関する詳細情報が表示されます。 DeveloperExceptionPageMiddlewareを使用して、HTTP パイプラインから同期例外と非同期例外をキャプチャし、エラー応答を生成します。 開発者例外ページはミドルウェア パイプラインの早い段階で実行されるため、次のミドルウェアでスローされたハンドルされない例外をキャッチできます。

ASP.NET Core アプリでは、次の両方の場合に開発者例外ページが既定で有効になります。

以前のテンプレートを使用して作成されたアプリ(つまり、 WebHost.CreateDefaultBuilderを使用して)は、 app.UseDeveloperExceptionPageを呼び出すことによって開発者例外ページを有効にすることができます。

Warnung

開発環境でアプリが実行されている場合を除き、開発者例外ページを有効にしないでください。 アプリが運用環境で実行されている場合は、詳細な例外情報をパブリックに共有しないでください。 環境の構成の詳細については、「 ASP.NET Core ランタイム環境」を参照してください。

開発者例外ページには、例外と要求に関する次の情報を含めることができます。

  • スタック トレース
  • クエリ文字列パラメーター (存在する場合)
  • Cookie (存在する場合)
  • Headers
  • エンドポイント メタデータ (存在する場合)

開発者例外ページでは、情報が提供されるとは限りません。 ログ記録を使用して、完全なエラー情報を確認します。

次の図は、タブと表示される情報を示すアニメーションを含むサンプル開発者例外ページを示しています。

選択した各タブを表示するようにアニメーション化された開発者例外ページ。

Accept: text/plain ヘッダーを持つ要求に応答して、開発者例外ページは HTML ではなくプレーン テキストを返します。 例えば次が挙げられます。

Status: 500 Internal Server Error
Time: 9.39 msSize: 480 bytes
FormattedRawHeadersRequest
Body
text/plain; charset=utf-8, 480 bytes
System.InvalidOperationException: Sample Exception
   at WebApplicationMinimal.Program.<>c.<Main>b__0_0() in C:\Source\WebApplicationMinimal\Program.cs:line 12
   at lambda_method1(Closure, Object, HttpContext)
   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)

HEADERS
=======
Accept: text/plain
Host: localhost:7267
traceparent: 00-0eab195ea19d07b90a46cd7d6bf2f

最小限の API で開発者例外ページを表示するには:

  • 開発環境でサンプル アプリを実行します。
  • /exception エンドポイントに移動します。

このセクションでは、最小 API で例外を処理する方法を示すために、次のサンプル アプリを参照します。 エンドポイント /exception が要求されると、例外がスローされます。

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/exception", () => 
{
    throw new InvalidOperationException("Sample Exception");
});

app.MapGet("/", () => "Test by calling /exception");

app.Run();

例外ハンドラー

開発環境以外では、 例外ハンドラー ミドルウェア を使用してエラー ペイロードを生成します。

Exception Handler Middlewareを構成するには、UseExceptionHandlerを呼び出します。 たとえば、次のコードは、 RFC 7807 準拠のペイロードでクライアントに応答するようにアプリを変更します。 詳細については、この記事の後半の「 問題の詳細 」セクションを参照してください。

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.UseExceptionHandler(exceptionHandlerApp 
    => exceptionHandlerApp.Run(async context 
        => await Results.Problem()
                     .ExecuteAsync(context)));

app.MapGet("/exception", () => 
{
    throw new InvalidOperationException("Sample Exception");
});

app.MapGet("/", () => "Test by calling /exception");

app.Run();

クライアントとサーバーのエラー応答

次の最小 API アプリについて考えてみましょう。

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/users/{id:int}", (int id) 
    => id <= 0 ? Results.BadRequest() : Results.Ok(new User(id)));

app.MapGet("/", () => "Test by calling /users/{id:int}");

app.Run();

public record User(int Id);

/users エンドポイントは、200 OKjsonより大きい場合はUserid表現した0を生成します。それ以外の場合は、応答本文のない400 BAD REQUEST状態コードを生成します。 応答の作成の詳細については、「 最小限の API アプリで応答を作成する」を参照してください。

Status Code Pages middlewareは、すべての HTTP クライアント (400-) またはサーバー (499500 -) 応答に対して、共通の本文コンテンツ (599) を生成するように構成できます。 ミドルウェアは、 UseStatusCodePages 拡張メソッドを呼び出すことによって構成されます。

たとえば、次の例では、ルーティング エラー ( など) を含むすべてのクライアントとサーバーの応答について、404 NOT FOUND 準拠のペイロードでクライアントに応答するようにアプリを変更します。 詳細については、「 問題の詳細 」セクションを参照してください。

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.UseStatusCodePages(async statusCodeContext 
    => await Results.Problem(statusCode: statusCodeContext.HttpContext.Response.StatusCode)
                 .ExecuteAsync(statusCodeContext.HttpContext));

app.MapGet("/users/{id:int}", (int id) 
    => id <= 0 ? Results.BadRequest() : Results.Ok(new User(id)) );

app.MapGet("/", () => "Test by calling /users/{id:int}");

app.Run();

public record User(int Id);

問題の詳細

問題の詳細 は、HTTP API エラーを記述する唯一の応答形式ではありません。ただし、HTTP API のエラーを報告するために一般的に使用されます。

問題の詳細サービスは、 IProblemDetailsService インターフェイスを実装します。これは、ASP.NET Core での問題の詳細の作成をサポートします。 AddProblemDetails(IServiceCollection)IServiceCollection拡張メソッドは、既定のIProblemDetailsService実装を登録します。

ASP.NET Core アプリでは、 AddProblemDetails が呼び出されたときに、次のミドルウェアによって問題の詳細 HTTP 応答が生成されます。ただし、 Accept 要求 HTTP ヘッダー に、登録された IProblemDetailsWriter でサポートされているコンテンツ タイプのいずれかが含まれていない場合 (既定値: application/json)。

最小限の API アプリは、拡張メソッドを使用してすべての HTTP クライアントおよびサーバー エラー応答に対する問題の詳細応答を生成するように構成できます。

次のコードは、問題の詳細を生成するようにアプリを構成します。

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddProblemDetails();

var app = builder.Build();

app.UseExceptionHandler();
app.UseStatusCodePages();

app.MapGet("/users/{id:int}", (int id) 
    => id <= 0 ? Results.BadRequest() : Results.Ok(new User(id)));

app.MapGet("/", () => "Test by calling /users/{id:int}");

app.Run();

public record User(int Id);

AddProblemDetailsの使用方法の詳細については、「問題の詳細」を参照してください。

IProblemDetailsService フォールバック

次のコードでは、httpContext.Response.WriteAsync("Fallback: An error occurred.")実装でIProblemDetailsServiceを生成できない場合、ProblemDetailsはエラーを返します。

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddProblemDetails();

var app = builder.Build();

app.UseExceptionHandler(exceptionHandlerApp =>
{
    exceptionHandlerApp.Run(async httpContext =>
    {
        var pds = httpContext.RequestServices.GetService<IProblemDetailsService>();
        if (pds == null
            || !await pds.TryWriteAsync(new() { HttpContext = httpContext }))
        {
            // Fallback behavior
            await httpContext.Response.WriteAsync("Fallback: An error occurred.");
        }
    });
});

app.MapGet("/exception", () =>
{
    throw new InvalidOperationException("Sample Exception");
});

app.MapGet("/", () => "Test by calling /exception");

app.Run();

前述のコード:

  • problemDetailsServiceProblemDetailsを記述できない場合は、フォールバック コードでエラー メッセージを書き込みます。 たとえば、 Accept 要求ヘッダー が、 DefaulProblemDetailsWriter がサポートしていないメディアの種類を指定するエンドポイントです。
  • 例外ハンドラー ミドルウェアを使用します。

次の例は、 Status Code Pages middlewareを呼び出す点を除き、前の例と似ています。

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddProblemDetails();

var app = builder.Build();

app.UseStatusCodePages(statusCodeHandlerApp =>
{
    statusCodeHandlerApp.Run(async httpContext =>
    {
        var pds = httpContext.RequestServices.GetService<IProblemDetailsService>();
        if (pds == null
            || !await pds.TryWriteAsync(new() { HttpContext = httpContext }))
        {
            // Fallback behavior
            await httpContext.Response.WriteAsync("Fallback: An error occurred.");
        }
    });
});

app.MapGet("/users/{id:int}", (int id) =>
{
    return id <= 0 ? Results.BadRequest() : Results.Ok(new User(id));
});

app.MapGet("/", () => "Test by calling /users/{id:int}");

app.Run();

public record User(int Id);

その他のエラー処理機能

コントローラーから最小限の API への移行

コントローラー ベースの API から最小 API に移行する場合:

  1. アクション フィルターをエンドポイント フィルターまたはミドルウェアに置き換える
  2. モデルの検証を手動検証またはカスタム バインドに置き換える
  3. 例外フィルターを例外処理ミドルウェアに置き換える
  4. 一貫性のあるエラー応答のためにを使用してAddProblemDetails()する

コントローラー ベースのエラー処理を使用する場合

必要に応じて、コントローラー ベースの API を検討してください。

  • 複雑なモデル検証シナリオ
  • 複数のコントローラー間での一元的な例外処理
  • エラー応答の書式設定をきめ細かく制御する
  • フィルターや規則などの MVC 機能との統合

検証エラー、問題の詳細のカスタマイズ、例外フィルターなど、コントローラーベースのエラー処理の詳細については、「 コントローラー 」タブのセクションを参照してください。

その他のリソース