Global Exception Handling & Middleware
15 questions found
How do you handle exceptions that occur during response streaming, after the response has already partially started sending to the client?
Advanced
Once HttpResponse.HasStarted is true (typically after the first write to the response body, common in streaming scenarios like Server-Sent Events or large file downloads), you can no longer modify the response status code or headers -- an exception occurring at this point can't be gracefully converted into a different error response shape, so exception handling middleware must check HasStarted and, if true, can only log the error and abruptly terminate the connection, making it critical to validate and prepare everything possible before any response writing begins.
public async ValueTask<bool> TryHandleAsync(HttpContext context, Exception exception, CancellationToken ct) {
if (context.Response.HasStarted) {
_logger.LogError(exception, "Exception after response started, cannot modify response");
return true; // acknowledge as 'handled' but can't change what's already been sent
}
context.Response.StatusCode = 500;
await context.Response.WriteAsJsonAsync(new { title = "An error occurred" }, ct);
return true;
}
Real-world example
A large file-export endpoint that fails midway through streaming can't return a clean error response to the client (since headers were already sent) -- the team adds thorough upfront validation to catch as many failure conditions as possible before streaming begins, minimizing this unavoidable limitation's impact.
Common follow-ups: How would a client detect and handle a connection that was abruptly terminated mid-stream?;What upfront validation strategies minimize the risk of mid-stream failures?
ASP.NET Core Middleware & Request Pipeline;gRPC Services
What is the role of the [ApiController] attribute in automatically handling certain error scenarios without explicit exception handling code?
Intermediate
[ApiController] enables several automatic API-friendly behaviors beyond just validation: automatic 400 responses for invalid model state, automatic binding source inference, and automatic problem details for certain error responses -- reducing boilerplate that would otherwise require manual exception handling or explicit checks in every action for common scenarios like malformed request bodies or missing required parameters.
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase {
[HttpPost]
public IActionResult Create(ProductDto dto) {
// No need to manually check ModelState.IsValid --
// [ApiController] already returned 400 automatically if dto was invalid
return Ok(_service.Create(dto));
}
}
Real-world example
A team migrating older MVC controllers (which required manual `if (!ModelState.IsValid) return BadRequest(ModelState);` checks in every action) to use [ApiController] removes dozens of duplicated validation-checking lines across their controllers.
Common follow-ups: What other automatic behaviors does [ApiController] enable besides validation short-circuiting?;How would you customize or disable this automatic behavior for specific scenarios?
RESTful Web APIs & Controllers;Diagnostics & Performance
How would you design exception handling middleware to support internationalized (localized) error messages returned to clients in different languages?
Advanced
The exception handler can use IStringLocalizer (or a similar localization service) combined with the request's Accept-Language header (parsed by ASP.NET Core's built-in request localization middleware) to select and return an error message in the client's preferred language, mapping exception types or error codes to localized resource string keys rather than hardcoding English-only messages directly in exception messages.
public async ValueTask<bool> TryHandleAsync(HttpContext context, Exception exception, CancellationToken ct) {
var localizer = context.RequestServices.GetRequiredService<IStringLocalizer<ErrorMessages>>();
var message = exception switch {
NotFoundException => localizer["ResourceNotFound"],
ValidationException => localizer["ValidationFailed"],
_ => localizer["UnexpectedError"]
};
await context.Response.WriteAsJsonAsync(new { message = message.Value }, ct);
return true;
}
Real-world example
A globally-distributed SaaS API returns error messages in the client's browser-configured language (French, Japanese, Spanish) by combining request localization middleware with a resource-file-backed error message lookup in its global exception handler.
Common follow-ups: How does ASP.NET Core's request localization middleware determine the preferred language?;What's the maintenance overhead of keeping error message translations in sync across many languages?
Configuration & Options;RESTful Web APIs & Controllers
What happens by default in ASP.NET Core if you don't configure any global exception handling middleware at all?
Beginner
Without any exception handling middleware configured, an unhandled exception propagates all the way up through Kestrel, resulting in either a bare 500 Internal Server Error with no body (in Production) or, if UseDeveloperExceptionPage is active, a detailed HTML error page showing the full stack trace -- neither is appropriate for a real production API, which is why configuring at least a basic UseExceptionHandler is considered essential baseline setup for any API expected to run in production.
// With NO exception handling middleware configured:
// An unhandled exception results in a generic 500 with an empty or minimal body
// -- no consistent JSON error format, no logging guarantee, no safe error message
Real-world example
A newly bootstrapped API prototype that skipped configuring UseExceptionHandler entirely is caught in code review before its first production deployment, since every unhandled exception would otherwise return an unhelpful bare 500 response to API consumers.
Common follow-ups: What's the minimum viable exception handling setup for a new ASP.NET Core API?;Why does Kestrel itself not provide a default JSON error format?
Global Exception Handling & Middleware;ASP.NET Core Middleware & Request Pipeline
How do you write an integration test verifying that your global exception handler produces the correct error response shape?
Intermediate
Using WebApplicationFactory<T> to spin up an in-memory test server, you can create a test-only endpoint or use an existing one that deliberately throws a known exception, then assert on the actual HTTP response's status code and JSON body shape returned by the real exception handling middleware -- verifying the full pipeline behavior rather than just unit testing the handler class in isolation.
public class ExceptionHandlingTests : IClassFixture<WebApplicationFactory<Program>> {
[Fact]
public async Task NotFoundException_Returns404WithProblemDetails() {
var client = _factory.CreateClient();
var response = await client.GetAsync("/api/products/99999");
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
var problem = await response.Content.ReadFromJsonAsync<ProblemDetails>();
Assert.Equal("Resource not found", problem.Title);
}
}
Real-world example
A team adds an integration test suite specifically verifying error response shapes for each custom exception type, catching a regression where a refactor accidentally changed the JSON field name from 'title' to 'Title', breaking client-side error parsing.
Common follow-ups: How does WebApplicationFactory differ from a full end-to-end test against a real deployed instance?;What's the value of testing the actual pipeline versus just unit testing the handler class directly?
Testing in .NET (xUnit
Integration & Unit Testing);Global Exception Handling & Middleware