Six common ASP.NET Core Web API mistakes and the patterns to use instead
A developer who has maintained ASP.NET Core APIs for years lists six recurring problems: scattered try/catch blocks, unbounded list queries, ignored cancellation tokens, lost UTC kinds, late config failures and unprotected login endpoints. Each comes with a suggested pattern using .NET 10 and EF Core 10, noted to work on .NET 8 with small changes.
Key points
- Handle errors in one IExceptionHandler that writes ProblemDetails; keep exception text out of 500 responses and add a traceId.
- On list endpoints combine AsNoTracking, DTO projection, OrderBy and a hard page-size cap validated with FluentValidation.
- Pass CancellationToken from the action down to EF Core calls such as ToListAsync(ct) and SaveChangesAsync(ct).
- Store DateTime as UTC and restore DateTimeKind.Utc with a value converter; use TimeProvider for audit timestamps.
- Bind options classes with ValidateDataAnnotations() and ValidateOnStart() so config mistakes fail at deploy time, and keep secrets out of git.
- Apply the built-in rate limiter to login, register and refresh endpoints, and configure Forwarded Headers when behind a proxy.
The author, who has spent about four and a half years building and maintaining ASP.NET Core APIs on SQL Server and Azure, catalogs six problems that work in demos but fail in production. The fixes centre on one global IExceptionHandler that returns ProblemDetails, so controllers stay thin and internal error text never reaches clients. For lists, the advice is to combine no-tracking queries, DTO projection, a stable OrderBy and a hard page-size cap.
The remaining points cover passing CancellationToken down to EF Core so abandoned requests stop querying, storing UTC with a value converter because datetime2 loses its kind, and validating typed options at startup with ValidateOnStart and separate liveness and readiness health checks. The last item is built-in rate limiting on login and refresh endpoints, with a reminder to configure Forwarded Headers behind a proxy. The original post contains the full code samples.
Real-world use
- A multi-controller enterprise API that needs consistent error responses and central logging.
- A product catalogue endpoint serving hundreds of thousands of rows, where paging and no-tracking queries cut memory and latency.
- A SaaS API with JWT login that must throttle password guessing and surface config errors during deploy.
Why it matters
Applying these patterns prevents many production issues before they happen: leaked stack traces, broken paging, wasted database work and config failures that only appear on the first request. Reviewing existing endpoints against this checklist is a practical starting point.