Pandatech.ResponseCrafter 7.1.0

dotnet add package Pandatech.ResponseCrafter --version 7.1.0
                    
NuGet\Install-Package Pandatech.ResponseCrafter -Version 7.1.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="Pandatech.ResponseCrafter" Version="7.1.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Pandatech.ResponseCrafter" Version="7.1.0" />
                    
Directory.Packages.props
<PackageReference Include="Pandatech.ResponseCrafter" />
                    
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 Pandatech.ResponseCrafter --version 7.1.0
                    
#r "nuget: Pandatech.ResponseCrafter, 7.1.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 Pandatech.ResponseCrafter@7.1.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=Pandatech.ResponseCrafter&version=7.1.0
                    
Install as a Cake Addin
#tool nuget:?package=Pandatech.ResponseCrafter&version=7.1.0
                    
Install as a Cake Tool

Pandatech.ResponseCrafter

Exception-based error handling for ASP.NET Core 8+ with RFC 9457-compliant ProblemDetails, structured logging, and SignalR support.

Note: If you prefer the Result pattern over exception-based error handling, consider **ResultCrafter ** by Haik Asatryan. ResultCrafter provides a more functional approach with explicit error handling through Result types. ResponseCrafter takes a different path—using typed exceptions with automatic ProblemDetails mapping—suitable for teams that prefer traditional exception flows.

Installation

dotnet add package Pandatech.ResponseCrafter

Setup

using ResponseCrafter.Enums;
using ResponseCrafter.Extensions;

builder.AddResponseCrafter(NamingConvention.ToSnakeCase);

// Optional: SignalR support
builder.Services.AddSignalR(o => o.AddFilter<SignalRExceptionFilter>());

var app = builder.Build();

app.UseResponseCrafter();

Quick Start

Throw Typed Exceptions

using ResponseCrafter.HttpExceptions;

app.MapPost("/users", (CreateUserRequest req) =>
{
    // Validation
    BadRequestException.ThrowIfNullOrWhiteSpace(req.Email, "email_required");
    
    // Not found
    var user = db.Users.Find(req.Id);
    NotFoundException.ThrowIfNull(user, "user_not_found");
    
    // Conflict
    if (db.Users.Any(u => u.Email == req.Email))
        throw new ConflictException("email_already_exists");
    
    // With field errors
    throw new BadRequestException("invalid_payload", new Dictionary<string, string>
    {
        ["email"] = "invalid_email_format",
        ["age"] = "must_be_18_or_older"
    });
});

Response Format

All errors return RFC 9457 ProblemDetails:

{
    "requestId": "0HN8K2MJ7F4QP:00000001",
    "traceId": "00-abc123...",
    "instance": "/api/users",
    "statusCode": 400,
    "type": "BadRequestException",
    "message": "invalid_payload",
    "errors": {
        "email": "invalid_email_format",
        "age": "must_be_18_or_older"
    }
}

Available Exceptions

Exception Status Static Helpers
BadRequestException 400 ✅ ThrowIf*, ThrowIfNull*, etc.
UnauthorizedException 401
PaymentRequiredException 402
ForbiddenException 403
NotFoundException 404
ConflictException 409
TooManyRequestsException 429
ForceToChangePasswordException 469
InternalServerErrorException 500
ServiceUnavailableException 503
GatewayTimeoutException 504

Static Helper Methods

All exceptions with static helpers provide:

// Null checks
NotFoundException.ThrowIfNull(user);
NotFoundException.ThrowIfNull(user, "custom_message");

// String checks
BadRequestException.ThrowIfNullOrWhiteSpace(input, "input_required");

// Collection checks
NotFoundException.ThrowIfNullOrEmpty(list, "no_results_found");

// Conditional
ForbiddenException.ThrowIf(condition, "access_denied");

// Numeric checks
BadRequestException.ThrowIfNullOrNegative(amount, "invalid_amount");

Naming Conventions

ResponseCrafter automatically converts error messages and field names to your preferred case:

builder.AddResponseCrafter(NamingConvention.ToSnakeCase);

Available conventions:

  • Default - No transformation
  • ToSnakeCase - user_not_found
  • ToCamelCase - userNotFound
  • ToPascalCase - UserNotFound
  • ToKebabCase - user-not-found
  • ToTitleCase - User Not Found
  • ToHumanCase - User not found
  • ToUpperSnakeCase - USER_NOT_FOUND

SignalR Support

Handle exceptions in SignalR hubs with automatic error broadcasting:

using ResponseCrafter.ExceptionHandlers.SignalR;

// Hub method
public class ChatHub : Hub<IChatClient>
{
    public Task SendMessage(Message request)
    {
        BadRequestException.ThrowIfNullOrWhiteSpace(request.Content, "message_empty");
        
        // Process message...
    }
}

// Request must implement IHubArgument
public class Message : IHubArgument
{
    public required string Content { get; set; }
    public required string InvocationId { get; set; }
}

Client receives error via ReceiveError event:

{
    "traceId": "00-abc123...",
    "invocationId": "unique-call-id",
    "instance": "SendMessage",
    "statusCode": 400,
    "message": "message_empty",
    "errors": null
}

Automatic Exception Mapping

ResponseCrafter automatically handles:

  • DbUpdateConcurrencyException → 409 Conflict
  • FluentImporter exceptions → 400 Bad Request (every ImportException, including subclasses added by future importer releases; MessageDetails is appended to the message, and MissingColumnsException / InvalidCellValueException also populate errors). The one exception is InvalidPropertyNameException → 500, because it means an import rule references a property the model does not have, which is a bug in your rule and not something the caller's file can cause.
  • Gridify exceptions → 400 Bad Request
  • BadHttpRequestException (malformed JSON) → 400 with parse details
  • Unhandled exceptions → 500 Internal Server Error

Error Visibility

Control error detail exposure via configuration:

{
    "ResponseCrafterVisibility": "Public"
}
  • Public (default): 5xx errors show generic message, 4xx show actual errors
  • Private: All errors show full details (dev/staging only)

OpenAPI Integration

app.MapPost("/users", (CreateUserRequest req) =>
{
    // ... handler logic
})
.ProducesBadRequest()
.ProducesConflict()
.ProducesNotFound();

Available extension methods:

  • ProducesBadRequest() - 400
  • ProducesUnauthorized() - 401
  • ProducesForbidden() - 403
  • ProducesNotFound() - 404
  • ProducesConflict() - 409
  • ProducesTooManyRequests() - 429
  • ProducesServiceUnavailable() - 503
  • ProducesPaymentRequired() - 402
  • ProducesErrorResponse(statusCode) - Custom status
  • ProducesErrorResponse(params int[]) - Multiple statuses

Advanced Usage

Custom Error Dictionaries

var errors = new Dictionary<string, string>
{
    ["field1"] = "error_message_1",
    ["field2"] = "error_message_2"
};

throw new BadRequestException("validation_failed", errors);
// or
throw new BadRequestException(errors); // Uses default message

Conditional Throwing

BadRequestException.ThrowIf(age < 18, "must_be_adult");
ForbiddenException.ThrowIf(!user.IsAdmin, "admin_only");

Comparison with ResultCrafter

Feature ResponseCrafter ResultCrafter
Approach Exception-based Result-based (functional)
Control flow try/catch Explicit Result<T>
Performance Exception overhead No exceptions
API clarity Implicit errors Explicit in signatures
Best for Traditional .NET teams Teams preferring functional style

Use ResponseCrafter if:

  • Your team is comfortable with exceptions
  • You have existing exception-based code
  • You prefer implicit error handling

Use ResultCrafter if:

  • You want explicit error handling in method signatures
  • You prefer functional programming patterns
  • Performance is critical (no exception overhead)

Configuration Reference

appsettings.json:

{
    "ResponseCrafterVisibility": "Public"
}

Program.cs:

builder.AddResponseCrafter(NamingConvention.ToSnakeCase);
app.UseResponseCrafter();

License

MIT

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  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.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on Pandatech.ResponseCrafter:

Package Downloads
Pandatech.SharedKernel

Opinionated ASP.NET Core 10 infrastructure kernel: OpenAPI (Swagger + Scalar), Serilog, MediatR, FluentValidation, CORS, SignalR, OpenTelemetry, health checks, maintenance mode, resilience pipelines, and shared utilities.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
7.1.0 122 7/26/2026
7.0.1 133 7/6/2026
7.0.0 235 2/28/2026
6.0.2 145 2/26/2026
6.0.1 173 1/26/2026
6.0.0 158 12/28/2025
5.3.1 224 12/23/2025
5.3.0 303 10/30/2025
5.2.2 310 9/8/2025
5.2.1 247 9/8/2025
5.2.0 235 8/15/2025
5.1.12 333 8/7/2025
5.1.11 221 7/30/2025
5.1.10 355 6/1/2025
5.1.9 331 4/9/2025
5.1.8 302 2/18/2025
5.1.7 252 2/17/2025
5.1.6 256 2/5/2025
5.1.5 264 1/10/2025
5.1.4 221 1/9/2025
Loading failed

FluentImporter mapping is now driven off the ImportException base type, so MissingColumnsException, DuplicateColumnException and SheetNotFoundException (added in FluentImporter 6.0.0) return 400 instead of falling through to 500. MessageDetails is no longer dropped, and MissingColumnsException/InvalidCellValueException now populate the errors dictionary. Behaviour change: InvalidPropertyNameException is now a 500 — it means a rule names a property the model does not have, which is a developer error rather than bad input. FluentImporter dependency bumped to 6.0.0.