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

在 session 結束時把已完成工作、待辦事項與經驗寫入交接檔案,並在 session 開始時重新載入。

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 個月前更新