次の方法で共有


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

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

Warnung

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

この記事では、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 機能との統合

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

その他のリソース