CoreDesign.ExceptionHandling 1.0.0

dotnet add package CoreDesign.ExceptionHandling --version 1.0.0
                    
NuGet\Install-Package CoreDesign.ExceptionHandling -Version 1.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="CoreDesign.ExceptionHandling" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="CoreDesign.ExceptionHandling" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="CoreDesign.ExceptionHandling" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add CoreDesign.ExceptionHandling --version 1.0.0
                    
#r "nuget: CoreDesign.ExceptionHandling, 1.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package CoreDesign.ExceptionHandling@1.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=CoreDesign.ExceptionHandling&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=CoreDesign.ExceptionHandling&version=1.0.0
                    
Install as a Cake Tool

CoreDesign.ExceptionHandling

CoreDesign.ExceptionHandling provides a single global exception handler for ASP.NET Core, producing consistent RFC 7807 ProblemDetails responses for unhandled exceptions. A Roslyn source generator collects every exception type marked with [ProblemMapping] and produces a compile-time dispatch table at build time. No reflection, no hand-maintained switch statement to keep in sync with your exception types.

This is a different concern from CoreDesign.Logging: the logging decorator logs and rethrows exceptions raised by decorated service methods, while CoreDesign.ExceptionHandling is the last line of defense at the edge of the HTTP pipeline, catching anything that escapes and turning it into a response the caller can act on.

Installation

dotnet add package CoreDesign.ExceptionHandling

Quick start

Register the handler at startup:

builder.Services.AddCoreDesignExceptionHandling();
builder.Services.AddGeneratedProblemMappings();
// ...
app.UseExceptionHandler();

AddGeneratedProblemMappings() is generated by the source generator and always available once the package is referenced, even with zero [ProblemMapping] attributes anywhere in the project. Call order between the two Add... calls does not matter.

With nothing else configured, every unhandled exception produces a generic RFC 7807 500 response. In Development, the response detail includes the exception; in every other environment it is omitted.

Mapping your own exceptions

Apply [ProblemMapping] directly to an exception type you own:

[ProblemMapping(404, Title = "Resource not found")]
public sealed class EntityNotFoundException(string entity, Guid id)
    : Exception($"{entity} '{id}' was not found.");

Throwing EntityNotFoundException anywhere in the request pipeline now produces a 404 response with detail set to the exception's Message.

Mapping exceptions you don't own

For BCL or third-party exception types, apply [ProblemMapping] at the assembly level instead, with ExceptionType set:

[assembly: ProblemMapping(408, ExceptionType = typeof(TaskCanceledException), Title = "Request timed out")]
[assembly: ProblemMapping(503, ExceptionType = typeof(SqlException), Title = "Database unavailable")]

One attribute, two places to put it, depending on whether you own the type.

Matching subtypes

By default a mapping also covers every subtype of the target exception, unless the subtype carries its own, more specific mapping:

[ProblemMapping(400, Title = "Domain rule violated")]
public class DomainException(string message) : Exception(message);

[ProblemMapping(404, Title = "Resource not found")]
public sealed class EntityNotFoundException(string entity, Guid id)
    : DomainException($"{entity} '{id}' was not found.");

Throwing an EntityNotFoundException produces a 404 (the more specific mapping wins). Throwing any other DomainException subtype produces a 400 (the base mapping applies). The generator orders the dispatch table by inheritance depth, most derived first, so this resolves correctly regardless of declaration order.

Set MatchDerived = false to restrict a mapping to the exact type only:

[ProblemMapping(422, Title = "Validation failed", MatchDerived = false)]
public class ValidationException(string message) : Exception(message);

A subtype of ValidationException with no mapping of its own now falls through to the generic 500 fallback instead of inheriting the 422 mapping.

Options

Property Default Effect
StatusCode required The HTTP status code written to the response.
Title null The RFC 7807 title. Falls back to the standard reason phrase for StatusCode when omitted.
Type null The RFC 7807 type URI. Omitted from the response when not set.
MatchDerived true Whether this mapping also covers subtypes that have no mapping of their own.
IncludeMessage true Whether Exception.Message is written to the response detail. Set to false for exceptions whose message should never reach a client.
ExceptionType null Required, and only meaningful, at the assembly level, to target a type the project does not own.

Compile-time safety

Two [ProblemMapping] attributes targeting the exact same concrete exception type is a build error (CDEH001), not a silent pick of one over the other. An [assembly: ProblemMapping] usage without ExceptionType set is also a build error (CDEH002).

Performance

Exception handling runs on the exceptional path, not on every request, so the generated dispatch table favors simplicity over the reflection-avoidance techniques used in CoreDesign.Logging: it is a plain C# type-pattern switch, which the compiler already turns into a sequence of direct type checks with no allocation and no runtime type lookup.

Dependencies

  • ASP.NET Core shared framework (IExceptionHandler, ProblemDetails)

Feedback

Feedback on this package is welcome. If you run into a missing feature, an unexpected behavior, or something that required more effort than it should have, open an issue at github.com/codyskidmore/CoreDesign/issues or tag @codyskidmore. Suggestions about missing features and priority input are especially appreciated.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net10.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0 46 7/27/2026

1.0.0
- Initial release