0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ASP.NET APIのエラーをよりスムーズに処理

Posted at

結果.問題は、ASP.NET Coreが提供する標準化されたエラー応答生成メソッドで、RFC 7807規格に準拠したエラー応答(問題の詳細)を作成します。これは、Web APIで構造化されたエラー情報を返すために使用され、タイプ、タイトル、ステータス、および詳細などのフィールドを含み、クライアントがエラーを理解し処理するのを助けます。パラメーターカスタマイズを通じて、結果.問題はエラータイプ、ステータスコード、詳細情報、およびインスタンスパスを明確に記述でき、APIの一貫性および保守性を向上させ、クライアントがエラーをより効率的に処理できるようにします。特に、最新のRESTful APIの構築に適しています。

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/test", () =>
{
    return Results.Problem(type: "Bad Request",
         title: "無効な番号",
         detail: "番号形式は:N00000、N+5桁の数字",
         statusCode: StatusCodes.Status400BadRequest);
});

app.Run();

実行結果:

結果画像

ProblemDetailsの拡張

以下のコードは、ASP.NET CoreでResults.Problemを拡張して、カスタムの標準化されたエラー応答を生成する方法を示しています。この拡張は、Problem Detailsフォーマットを使用し、エラーの詳細の処理方法をカスタマイズすることで、エラー情報の構造化とデバッグ性を強化します。

まず、builder.Services.AddProblemDetailsを通じてProblem Detailsサービスを登録し、CustomizeProblemDetails構成でProblem Detailsの内容をカスタマイズします。この構成では、エラーインスタンス(Instance)の詳細な形式が指定され、リクエストプロトコル、メソッド、およびパスを含みます。同時に、requestId(リクエストのユニークな識別子)、traceId(トレース識別子)、およびclientIP(クライアントのIPアドレス)などのクライアントリクエストのトレース情報を追加し、エラー応答にコンテキスト情報を持たせます。

カスタム例外タイプをキャッチおよび処理するために、コードはProblemExceptionという名前のカスタム例外クラスを作成しました。このクラスには、特定のエラー情報を保存するためのErrorおよびMessageプロパティがあります。続いて、IExceptionHandlerインターフェースを実装したProblemExceptionHandlerクラスを実装し、このクラスは例外をインターセプトし、ProblemDetailsServiceを通じて標準のProblem Details応答を生成します。この過程で、TryHandleAsyncメソッドはProblemExceptionの例外かどうかを確認し、それがそうであれば、ステータスを400(Bad Request)に設定し、ProblemExceptionインスタンスのErrorおよびMessageプロパティに基づいてTitleおよびDetailフィールドを埋め込んだカスタムのProblemDetailsオブジェクトを作成します。最後に、ProblemDetailsService.TryWriteAsyncメソッドを通じてProblem Detailsを応答に書き込み、クライアントが構造化されたエラー情報を確実に受信できるようにします。

アプリケーション内で、/testというルートを定義し、このルートに対するクライアントリクエスト時にProblemException例外をスローし、カスタムエラー情報を含むProblem Details応答を返します。この方法を通じて、クライアントは詳細なエラーコンテキストおよび構造化されたエラー情報を受信でき、問題を迅速に識別およびデバッグするのに役立ちます。

具体的なコード:

using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddProblemDetails(opt =>
{
    opt.CustomizeProblemDetails = context =>
    {
        context.ProblemDetails.Instance = $"{context.HttpContext.Request.Protocol} {context.HttpContext.Request.Method} {context.HttpContext.Request.Path}";
        context.ProblemDetails.Extensions.TryAdd("requestId", context.HttpContext.TraceIdentifier);

        var activity = context.HttpContext.Features.Get<IHttpActivityFeature>()?.Activity;
        context.ProblemDetails.Extensions.TryAdd("traceId", activity?.Id);

        if (context.HttpContext.Connection != null && context.HttpContext.Connection.RemoteIpAddress != null)
        {
            context.ProblemDetails.Extensions.TryAdd("clientIP", context.HttpContext.Connection.RemoteIpAddress?.ToString());
        }
    };
});
builder.Services.AddExceptionHandler<ProblemExceptionHandler>();

var app = builder.Build();

app.MapGet("/test", () =>
{
    throw new ProblemException(error: "無効な番号",
         message: "番号形式は:N00000、N+5桁の数字");
});
app.UseExceptionHandler();
app.Run();

public class ProblemException : Exception
{
    public string Error { get; }
    public ProblemException(string error, string message) : base(message)
    {
        Error = error;
    }
}
public class ProblemExceptionHandler : IExceptionHandler
{
    private readonly IProblemDetailsService _problemDetailsService;
    public ProblemExceptionHandler(IProblemDetailsService problemDetailsService)
    {
        _problemDetailsService = problemDetailsService;
    }
    public async ValueTask<bool> TryHandleAsync(HttpContext httpContext, Exception exception, CancellationToken cancellationToken)
    {
        if (exception is not ProblemException problemException)
        {
            return true;
        }
        var problemDetails = new ProblemDetails
        {
            Status = StatusCodes.Status400BadRequest,
            Title = problemException.Error,
            Detail = problemException.Message,
            Type = "Bad Request"
        };
        httpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
        return await _problemDetailsService.TryWriteAsync(new ProblemDetailsContext
        {
            HttpContext = httpContext,
            ProblemDetails = problemDetails
        });
    }
}

実装の結果:

実装画像

実装した際の欠点は、例外がスローされると、プラットフォームが失敗のログを出力することです。

ログ画像

(Translated by GPT)

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?