はじめに
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") など、既存の仕組を利用するほうがシンプルだと思います。