Skip to main content
CodeOath
← All posts

.NET Core / Web API71 min total · 19 parts

Building REST APIs with ASP.NET Core: Routing, Middleware, and Dependency Injection

Part 14 of 19 · ~1 min

Error Handling and ProblemDetails

An unhandled exception's raw message making it back to a caller leaks internals — a connection string embedded in an error, an internal type name, a stack trace — and hands the client nothing structured to actually act on. Bench centralizes this into exception-handling middleware:

if (app.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage(); // full detail — local development only
}
else
{
    app.UseExceptionHandler(errorApp =>
    {
        errorApp.Run(async context =>
        {
            context.Response.StatusCode = StatusCodes.Status500InternalServerError;
            context.Response.ContentType = "application/problem+json";
            await context.Response.WriteAsJsonAsync(new ProblemDetails
            {
                Status = 500,
                Title = "Something went wrong on Bench's end.",
                // deliberately no exception message or stack trace sent to the client
            });
        });
    });
}

[ApiController]'s own validation failures already come back shaped as ProblemDetails, at no extra cost — carrying that same shape over to unhandled exceptions means a caller who already wrote code to read a 400 doesn't need a separate code path for a 500. A bad CreateReservationRequest and something genuinely broken on Bench's end read like siblings on the wire, not like two unrelated APIs happened to share a domain.

The reservation conflict from a few chapters back deserves a proper ProblemDetails body too, once it's caught correctly:

if (conflict) return Conflict(new ProblemDetails
{
    Status = 409,
    Title = "Reservation conflict",
    Detail = "This tool is already booked for an overlapping window."
});

That's what a 409 should have looked like for Marcus and Yuki, and it's the shape Bench's kiosk UI is actually built to display — a real message, not a silent success screen.