Error Handling

codewithmukesh/dotnet-claude-kit/skills/error-handling

作者 codewithmukesh23300897f4d1无许可证754 个星标收录于 2026年10月8日更新于 2026年10月8日仓库2个月前更新

Error handling strategy for .NET 10 applications. Covers the Result pattern, ProblemDetails (RFC 9457), global exception handling, FluentValidation, and structured error responses. Load this skill when implementing error handling, validation, or designing API error contracts, or when the user mentions "error handling", "Result pattern", "ProblemDetails", "exception", "validation", "FluentValidation", "error response", "global exception handler", or "RFC 9457".

AI 生成的概览

为 .NET 10 应用提供错误处理、验证与 API 错误契约的实现指导。

功能
该技能说明 .NET 10 应用的错误处理策略,涵盖 Result 模式、符合 RFC 9457 的 ProblemDetails、全局异常处理、配合终结点筛选器的 FluentValidation,以及类型化错误结果。它提供结果类型、将结果与错误映射为 HTTP 响应、验证筛选器的 C# 代码示例,并附有决策指南。它还列出反模式,例如用异常控制流程、返回原始错误字符串以及吞掉异常。
适用场景
在 .NET API 中实现错误处理或验证时,或设计 API 错误契约时使用。当用户提到错误处理、Result 模式、ProblemDetails、异常、验证、FluentValidation、错误响应、全局异常处理程序或 RFC 9457 时也适用。
运行要求
该技能不附带脚本或资源,仅为说明与代码示例。应用这些示例需要 .NET 10 项目、ASP.NET Core 最小 API 以及 FluentValidation 包。

Error Handling

Core Principles

  1. Use the Result pattern for expected failures — Don't throw exceptions for things like "order not found" or "validation failed". These are expected outcomes, not exceptional conditions. See ADR-002.
  2. Reserve exceptions for unexpected failures — Database connection lost, null reference bugs, network timeouts — these are truly exceptional and should propagate to the global handler.
  3. Every API error returns ProblemDetails — RFC 9457 is the standard. Every error response has type, title, status, detail, and optionally errors.
  4. Validate at the boundary — Validate incoming requests at the API layer, not deep inside business logic.

Patterns

Result Pattern

A simple, generic result type that carries either a value or errors.

csharp
public class Result{    public bool IsSuccess { get; }    public bool IsFailure => !IsSuccess;    public List<string> Errors { get; }
    protected Result(bool isSuccess, List<string>? errors = null)    {        IsSuccess = isSuccess;        Errors = errors ?? [];    }
    public static Result Success() => new(true);    public static Result Failure(params string[] errors) => new(false, [..errors]);    public static Result<T> Success<T>(T value) => new(value);    public static Result<T> Failure<T>(params string[] errors) => new(errors);}
public class Result<T> : Result{    public T Value { get; }
    internal Result(T value) : base(true) => Value = value;    internal Result(IEnumerable<string> errors) : base(false, [..errors]) => Value = default!;}

Result to ProblemDetails Mapping

csharp
public static class ResultExtensions{    public static IResult ToProblemDetails(this Result result, int statusCode = 400)    {        return TypedResults.Problem(            title: "One or more errors occurred",            statusCode: statusCode,            extensions: new Dictionary<string, object?>            {                ["errors"] = result.Errors            });    }}
// Usage in endpointgroup.MapPost("/", async (CreateOrder.Command command, ISender sender, CancellationToken ct) =>{    var result = await sender.Send(command, ct);    return result.IsSuccess        ? TypedResults.Created($"/api/orders/{result.Value.Id}", result.Value)        : result.ToProblemDetails();});

Global Exception Handler

Catches unexpected exceptions and converts them to ProblemDetails. For the modern IExceptionHandler approach (preferred), see knowledge/common-infrastructure.md. The inline lambda below works for simple cases:

csharp
// Program.csapp.UseExceptionHandler(errorApp =>{    errorApp.Run(async context =>    {        var exception = context.Features.Get<IExceptionHandlerFeature>()?.Error;        var logger = context.RequestServices.GetRequiredService<ILogger<Program>>();
        logger.LogError(exception, "Unhandled exception for {Method} {Path}",            context.Request.Method, context.Request.Path);
        var problem = new ProblemDetails        {            Title = "An unexpected error occurred",            Status = StatusCodes.Status500InternalServerError,            Type = "https://tools.ietf.org/html/rfc9110#section-15.6.1"        };
        // Don't leak details in production        if (context.RequestServices.GetRequiredService<IHostEnvironment>().IsDevelopment())        {            problem.Detail = exception?.Message;        }
        context.Response.StatusCode = problem.Status.Value;        await context.Response.WriteAsJsonAsync(problem);    });});

FluentValidation with Endpoint Filters

csharp
// Validatorpublic class CreateOrderValidator : AbstractValidator<CreateOrderRequest>{    public CreateOrderValidator()    {        RuleFor(x => x.CustomerId)            .NotEmpty().WithMessage("Customer ID is required");
        RuleFor(x => x.Items)            .NotEmpty().WithMessage("At least one item is required");
        RuleForEach(x => x.Items).ChildRules(item =>        {            item.RuleFor(x => x.ProductId).NotEmpty();            item.RuleFor(x => x.Quantity).GreaterThan(0);        });    }}
// Generic validation filterpublic class ValidationFilter<TRequest> : IEndpointFilter{    public async ValueTask<object?> InvokeAsync(        EndpointFilterInvocationContext context,        EndpointFilterDelegate next)    {        var validator = context.HttpContext.RequestServices.GetService<IValidator<TRequest>>();        if (validator is null)            return await next(context);
        var request = context.Arguments.OfType<TRequest>().FirstOrDefault();        if (request is null)            return await next(context);
        var result = await validator.ValidateAsync(request);        if (!result.IsValid)        {            return TypedResults.ValidationProblem(result.ToDictionary());        }
        return await next(context);    }}
// Registrationgroup.MapPost("/", CreateOrder)    .AddEndpointFilter<ValidationFilter<CreateOrderRequest>>();

Typed Error Results

For richer error handling, use typed error enums or error objects.

csharp
public abstract record Error(string Code, string Message);public record NotFoundError(string Entity, object Id)    : Error("not_found", $"{Entity} with ID {Id} was not found");public record ValidationError(string Field, string Message)    : Error("validation", Message);public record ConflictError(string Message)    : Error("conflict", Message);
// Map to HTTP status codespublic static IResult ToHttpResult(this Error error) => error switch{    NotFoundError => TypedResults.Problem(title: error.Message, statusCode: 404),    ValidationError => TypedResults.Problem(title: error.Message, statusCode: 400),    ConflictError => TypedResults.Problem(title: error.Message, statusCode: 409),    _ => TypedResults.Problem(title: error.Message, statusCode: 500)};

Anti-patterns

Don't Throw Exceptions for Flow Control

csharp
// BAD — exceptions for expected outcomespublic Order GetOrder(Guid id){    var order = db.Orders.Find(id)        ?? throw new NotFoundException($"Order {id} not found");    return order;}
// GOOD — Result patternpublic Result<Order> GetOrder(Guid id){    var order = db.Orders.Find(id);    return order is not null        ? Result.Success(order)        : Result.Failure<Order>($"Order {id} not found");}

Don't Return Raw Error Strings from APIs

csharp
// BAD — inconsistent error formatreturn Results.BadRequest("Something went wrong");return Results.BadRequest(new { error = "Invalid input" });
// GOOD — always ProblemDetailsreturn TypedResults.Problem(title: "Invalid input", statusCode: 400);return TypedResults.ValidationProblem(validationResult.ToDictionary());

Don't Catch and Swallow Exceptions

csharp
// BAD — silently swallowingtry { await ProcessOrder(order); }catch (Exception) { /* ignore */ }
// GOOD — log and handle appropriatelytry { await ProcessOrder(order); }catch (PaymentException ex){    logger.LogWarning(ex, "Payment failed for order {OrderId}", order.Id);    return Result.Failure<Order>("Payment processing failed");}

Decision Guide

ScenarioRecommendation
Expected business failureResult pattern
Input validationFluentValidation with endpoint filter
Unexpected crashGlobal exception handler → ProblemDetails
API error formatRFC 9457 ProblemDetails — always
Validation in handlerReturn Result.Failure, don't throw
External service failureCatch specific exception, return Result.Failure
Logging errorsStructured logging with correlation ID

来源与署名

来源:codewithmukesh/dotnet-claude-kit位于skills/error-handling提交2330089

许可证: 无许可证

内容归原作者所有。SourceWeft 从公开仓库中收录这些内容。

举报或申请下架

更多来自 codewithmukesh/dotnet-claude-kit 的技能

Wrap Up

codewithmukesh

在会话结束时把已完成工作、待办任务与经验写入交接文件,并在会话开始时重新载入。

Productivity & Workflow7542个月前更新

Workflow Mastery

codewithmukesh

Claude Code workflow mastery for .NET developers. Covers parallel execution with git worktrees, plan mode strategy, verification loops, auto-formatting hooks, permission setup for dotnet CLI, prompting techniques, subagent patterns, and context discipline — token budget management, MCP-first navigation, lazy loading, and subagent isolation — all adapted for the .NET ecosystem. Load this skill when setting up Claude Code for a .NET project, optimizing workflows, running parallel sessions, when context is running low or sessions feel sluggish, when exploring a large codebase efficiently, or when the user mentions "productivity", "workflow", "parallel", "worktree", "plan mode", "permissions", "hooks", "10x", "setup Claude Code", "speed up development", "context", "tokens", "budget", "running out of context", "too many files", or "large codebase". Inspired by tips from Boris Cherny (creator of Claude Code) and the Anthropic team.

待分类7542个月前更新

Vertical Slice

codewithmukesh

指导 .NET 开发者采用垂直切片架构组织应用,涵盖功能文件夹、端点分组与处理程序模式。

Software Development7542个月前更新

Testing

codewithmukesh

Testing strategy for .NET 10 applications. Covers xUnit v3, WebApplicationFactory for integration tests, Testcontainers for real database testing, Verify for snapshot testing, and the AAA pattern. Load this skill when writing tests, setting up test infrastructure, reviewing test coverage, or when the user mentions "test", "xUnit", "WebApplicationFactory", "Testcontainers", "integration test", "unit test", "bUnit", "snapshot test", "Verify", "test coverage", "AAA pattern", "WireMock", or "FakeTimeProvider".

待分类7542个月前更新

Tdd

codewithmukesh

Guided test-driven development workflow for .NET 10 using xUnit v3, WebApplicationFactory, Testcontainers, and Verify snapshots. Follows the strict red-green-refactor cycle. Use when: "TDD", "test-driven", "let's TDD this", "red green refactor", "write the test first", or when building a feature with clear acceptance criteria.

待分类7542个月前更新

Spec

codewithmukesh

通过结构化提问,把模糊的功能想法转化为双方确认并持久化的规格说明文件。

Productivity & Workflow7542个月前更新