Từ .NET 8, cách chuẩn là IExceptionHandler kết hợp AddProblemDetails, thay cho việc rải try/catch trong controller.
csharp
public sealed class GlobalExceptionHandler(ILogger<GlobalExceptionHandler> logger)
: IExceptionHandler
{
public async ValueTask<bool> TryHandleAsync(
HttpContext ctx, Exception ex, CancellationToken ct)
{
logger.LogError(ex, "Unhandled exception on {Path}", ctx.Request.Path);
var problem = new ProblemDetails
{
Status = ex is NotFoundException ? StatusCodes.Status404NotFound
: StatusCodes.Status500InternalServerError,
Title = "An error occurred",
Instance = ctx.Request.Path,
};
ctx.Response.StatusCode = problem.Status.Value;
await ctx.Response.WriteAsJsonAsync(problem, ct);
return true; // false -> pass to the next handler
}
}
builder.Services.AddProblemDetails();
builder.Services.AddExceptionHandler<GlobalExceptionHandler>();
// ...
app.UseExceptionHandler();Các điểm cần nắm:
- ProblemDetails là RFC 7807 — client có cấu trúc cố định (type, title, status, detail, instance) để parse, không phải mỗi endpoint một kiểu.
- Có thể đăng ký nhiều handler, chúng chạy theo thứ tự đăng ký; trả false để nhường cho handler sau.
- Handler có lifetime singleton — không inject dịch vụ scoped trực tiếp.
- Không đưa ex.ToString() vào response ở production; log stack trace phía server, trả cho client một traceId để đối chiếu.
So với middleware try/catch viết tay: cùng ý tưởng nhưng có sẵn tích hợp ProblemDetails và cơ chế chuỗi handler.