Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

This article is a Private article. Only a writer and users who know the URL can access it.
Please change open range to public in publish setting if you want to share this article with other users.

ASP.NET Core Web API で未処理例外を捕捉する【IExceptionHandler】

0
Posted at

はじめに

ASP.NET Core Web API での未処理例外を IExceptionHandler で捕捉する備忘録です。

改訂履歴

  • 2026/08/15 : 初版公開。

本文

1. 環境

  • .NET 8.0
  • Visual Studio Community 2022

2. 実装

IExceptionHandler を実装した GlobalExceptionHandler を作ります。

このハンドラーで未処理例外を処理します。捕捉した例外を switch 式で分岐して処理しているところですが、もし例外数が多くなるなら、個別のハンドラーを作成する方法で対応した方が良いです。

GlobalExceptionHandler.cs
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Mvc;

namespace IExceptionHandlerExample;

/// <summary>
/// 未処理例外を処理するハンドラー。
/// </summary>
/// <param name="logger">ロガー。</param>
/// <param name="problemDetailsService"><see cref="ProblemDetails"/> レスポンスを作成する機能を提供するサービス。</param>
internal sealed class GlobalExceptionHandler(
    ILogger<GlobalExceptionHandler> logger,
    IProblemDetailsService problemDetailsService)
    : IExceptionHandler
{
    /// <inheritdoc/>
    public async ValueTask<bool> TryHandleAsync(
        HttpContext httpContext,
        Exception exception,
        CancellationToken cancellationToken)
    {
        // エラーログを出力する。
        logger.LogError(
            exception,
            "An unhandled exception occurred. {Path}",
            httpContext.Request.Path);

        // 例外毎に ProblemDetails を作成する。
        ProblemDetails problemDetails = exception switch
        {
            ValidationException validationException => new ProblemDetails
            {
                Status = StatusCodes.Status400BadRequest,
                Title = "Validation Error",
                Detail = validationException.Message
            },
            _ => new ProblemDetails
            {
                Status = StatusCodes.Status500InternalServerError,
                Title = "Internal Server Error",
                Detail = "An unexpected error occurred."
            }
        };

        // 障害調査用の TraceId を追加する。
        problemDetails.Extensions["traceId"] = httpContext.TraceIdentifier;

        // HTTP ステータスコードを設定する。
        httpContext.Response.StatusCode = problemDetails.Status ?? StatusCodes.Status500InternalServerError;

        // ProblemDetails を返す。
        return await problemDetailsService.TryWriteAsync(
            new ProblemDetailsContext
            {
                HttpContext = httpContext,
                Exception = exception,
                ProblemDetails = problemDetails
            });
    }
}

先程の GlobalExceptionHandler が使えるように、エントリポイントで登録します。

Program.cs
namespace IExceptionHandlerExample;

/// <summary>
/// アプリケーションのエントリポイントを定義します。
/// </summary>
public class Program
{
    /// <summary>
    /// アプリケーションのエントリポイントです。
    /// </summary>
    public static void Main(string[] args)
    {
        WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
        builder.Services.AddControllers();
        builder.Services.AddEndpointsApiExplorer();
        builder.Services.AddSwaggerGen();

+       // ProblemDetails と未処理例外のハンドラーを登録する。
+       builder.Services.AddProblemDetails();
+       builder.Services.AddExceptionHandler<GlobalExceptionHandler>();

        WebApplication app = builder.Build();

        if (app.Environment.IsDevelopment())
        {
            app.UseSwagger();
            app.UseSwaggerUI();
        }

+       // 未処理例外のハンドラーを使用する。
+       app.UseExceptionHandler();

        app.UseHttpsRedirection();
        app.UseAuthorization();
        app.MapControllers();
        app.Run();
    }
}

3. 動作確認

動作確認用のエンドポイントを作りました。

WeatherForecastController.cs
[Produces("application/json")]
[HttpGet("exception")]
public IEnumerable<WeatherForecast> GetWithException()
{
    throw new Exception("This is a test exception.");
}

リクエストの結果は以下です。いけてそうですね。

Response body
{
  "type": "https://tools.ietf.org/html/rfc9110#section-15.6.1",
  "title": "Internal Server Error",
  "status": 500,
  "detail": "An unexpected error occurred.",
  "traceId": "0HNNQM7HUL48P:00000009"
}
Response headers
cache-control: no-cache,no-store 
content-type: application/problem+json 
date: Sat,15 Aug 2026 09:45:12 GMT 
expires: -1 
pragma: no-cache 
server: Kestrel 

おわりに

MVC でも IExceptionHandler は利用できますが、例外発生時にエラーページを表示するのであれば、UseExceptionHandler("/Home/Error") など、既存の仕組を利用するほうがシンプルだと思います。

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?