Introduction
ShopNest's public API must return consistent, safe error responses — never stack traces to clients. This lesson covers ProblemDetails (RFC 7807), global exception handlers, and .NET 8's IExceptionHandler.
After this article you will
- Use Developer Exception Page vs production handler
- Return ProblemDetails from API errors
- Build custom exception hierarchy
- Implement IExceptionHandler (.NET 8)
- Map validation failures to 400 with field errors
Prerequisites
- Article 27 — Logging
- ShopNest.Web with EF Core from Module 2
Concept deep-dive
// .NET 8 global handler
public class GlobalExceptionHandler : IExceptionHandler
{
private readonly ILogger<GlobalExceptionHandler> _logger;
public GlobalExceptionHandler(ILogger<GlobalExceptionHandler> logger)
=> _logger = logger;
public async ValueTask<bool> TryHandleAsync(
HttpContext ctx, Exception ex, CancellationToken ct)
{
_logger.LogError(ex, "Unhandled exception {Path}", ctx.Request.Path);
var (status, title) = ex switch
{
NotFoundException => (404, "Not Found"),
ValidationException => (400, "Validation Failed"),
UnauthorizedAccessException => (403, "Forbidden"),
_ => (500, "Internal Server Error")
};
var problem = new ProblemDetails
{
Status = status,
Title = title,
Detail = ctx.RequestServices.GetRequiredService<IHostEnvironment>()
.IsDevelopment() ? ex.Message : null,
Instance = ctx.Request.Path
};
ctx.Response.StatusCode = status;
await ctx.Response.WriteAsJsonAsync(problem, ct);
return true;
}
}
builder.Services.AddExceptionHandler<GlobalExceptionHandler>();
builder.Services.AddProblemDetails();
app.UseExceptionHandler();
| Code | When |
|---|---|
| 400 | Validation, bad input |
| 401 | Not authenticated |
| 403 | Authenticated but forbidden |
| 404 | Resource missing |
| 409 | Conflict (duplicate, concurrency) |
| 500 | Unexpected server error — log full exception |
Hands-on — ShopNest Public-Facing REST API
- NotFoundException, ValidationException domain types.
- Register GlobalExceptionHandler; remove try-catch from controllers.
- API returns application/problem+json body on errors.
- ValidationException includes errors dictionary extension.
Common errors & best practices
- Returning ex.Message to clients in production — information leak.
- try-catch in every action — use global handler instead.
- 500 for validation errors — use 400 with ProblemDetails extensions.
Interview questions
Q: ProblemDetails?
A: RFC 7807 standard JSON error shape — type, title, status, detail, instance.
Q: IExceptionHandler vs middleware?
A: IExceptionHandler is the .NET 8+ extensible hook integrated with ProblemDetails.
Q: Developer Exception Page when?
A: Development only — never in Production.
Summary
- Global handlers replace scattered try-catch
- ProblemDetails is the API error standard
- Custom exceptions map to correct HTTP status codes
- Always log exceptions with correlation ID before responding
Previous: Logging
Next: ASP.NET Core Identity
FAQ
MVC views vs API errors?
MVC uses UseExceptionHandler with error.cshtml; APIs use ProblemDetails JSON.
ModelState invalid without exception?
return ValidationProblem(ModelState) — built-in ProblemDetails helper.