Roadmap › .NET Full Stack Developer Interview — End to End › Module: End-to-End Request Flow
Lessons in this course (10)

5. Validation, error handling and logging across the layers

Intermediate★ Must knowLesson 5 of 10

Validate at every boundary — for different reasons:

LayerValidationWhy
UIReactive form validators / schema (Zod)Fast feedback for the user
APIDataAnnotations or FluentValidation; [ApiController] returns 400 automaticallyNever trust the client
Domain/serviceBusiness rules (credit limit, status transitions)Rules that need data
DatabaseNOT NULL, FK, UNIQUE, CHECK constraintsLast line of defence for integrity

Consistent errors with ProblemDetails (RFC 9457):

csharp
builder.Services.AddProblemDetails();
builder.Services.AddExceptionHandler<GlobalExceptionHandler>();
app.UseExceptionHandler();

public sealed class GlobalExceptionHandler(ILogger<GlobalExceptionHandler> log) : IExceptionHandler
{
    public async ValueTask<bool> TryHandleAsync(HttpContext ctx, Exception ex, CancellationToken ct)
    {
        var (status, title) = ex switch
        {
            NotFoundException => (404, "Not found"),
            ValidationException => (400, "Validation failed"),
            _ => (500, "Unexpected error")
        };
        if (status == 500) log.LogError(ex, "Unhandled error for {Path}", ctx.Request.Path);
        ctx.Response.StatusCode = status;
        await ctx.Response.WriteAsJsonAsync(new ProblemDetails { Status = status, Title = title }, ct);
        return true;
    }
}

UI side: one HTTP interceptor maps 400 → show field errors, 401 → refresh/login, 403 → "not allowed", 500 → toast + correlation id.

Logging: structured logs ({OrderId} placeholders, not string concatenation), a correlation id per request passed from UI to API to downstream services, log levels used properly, no passwords/tokens/PII in logs.