Pandatech.ResponseCrafter 5.2.0

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

Pandatech.ResponseCrafter

A lightweight exception handling and logging package for ASP.NET Core (Minimal APIs with first-class SignalR support.
It standardizes server-side error handling and produces consistent, frontend-friendly error payloads with messages suitable for localization.


Features

  • Unified exception handling for REST & SignalR (built on ASP.NET Core’s IExceptionHandler and SignalR IHubFilter).
  • Visibility modes (Public / Private) to control how much detail is exposed in responses (e.g., hide 5xx details in Public).
  • Frontend-friendly messages via configurable naming conventions (e.g., snake_case).
  • Predefined HTTP exceptions covering common 4xx/5xx cases + helpers (ThrowIf...) to reduce boilerplate.
  • Consistent logging with correlation IDs (RequestId, TraceId) and level split (4xx → warning, 5xx → error).
  • SignalR error envelope with invocation_id echo and ReceiveError channel.

Installation

Use either NuGet Package Manager or the CLI:

dotnet add package Pandatech.ResponseCrafter
# or
Install-Package Pandatech.ResponseCrafter

Quick Start (Minimal API)

program.cs

var builder = WebApplication.CreateBuilder(args);

// 1) Register ResponseCrafter (optional naming convention)
builder.AddResponseCrafter(NamingConvention.ToSnakeCase);

// 2) Configure SignalR (optional). The filter applies package behavior to hubs.
builder.Services.AddSignalR(options => options.AddFilter<SignalRExceptionFilter>());

var app = builder.Build();

// 3) Use ResponseCrafter middleware/exception handler
app.UseResponseCrafter();

app.MapGet("/ping", () => "pong");

app.MapHub<ChatHub>("/hubs/chat");

app.Run();

appsettings.json

{
    "ResponseCrafterVisibility": "Public"
}
  • Public: 4xx → detailed as defined; 5xx → generic message (no sensitive details).
  • Private: 4xx/5xx → expands with verbose diagnostics (where available).

Note: By default, ResponseCrafter suppresses the duplicate framework error log from Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware. If you prefer to keep that log, opt out:

builder.AddResponseCrafter(> NamingConvention.ToSnakeCase,> suppressExceptionHandlerMiddlewareLog: false);
---

## Supported HTTP Status Codes

| Code | Description                                       |
|:-----|:--------------------------------------------------|
| 200  | Request succeeded.                                |
| 202  | Request accepted (e.g. order enqueued).           |
| 400  | Invalid request parameters or duplicate requests. |
| 401  | Authentication failed.                            |
| 403  | Insufficient permissions.                         |
| 404  | Resource not found.                               |
| 409  | Conflict (e.g. concurrency violation).            |
| 429  | Too many requests.                                |
| 500  | Server encountered an unexpected error.           |
| 503  | Service unavailable.                              |

---

### REST (HTTP/JSON)

**Content type:** `application/json`

```json
{
    "RequestId": "0HMVFE0A284AM:00000001",
    "TraceId": "a55582ab204162e66e124b0378776ab7",
    "Instance": "POST - api.example.com:443/users/register",
    "StatusCode": 400,
    "Type": "BadRequestException",
    "Errors": {
        "email": "email_address_is_not_in_a_valid_format",
        "password": "password_must_be_at_least_8_characters_long"
    },
    "Message": "the_request_was_invalid_or_cannot_be_otherwise_served."
}

Fields

  • RequestId – ASP.NET Core HttpContext.TraceIdentifier.
  • TraceId – distributed trace id (W3C, e.g., Activity.Current?.TraceId).
  • Instance – contextual info (e.g., METHOD - host:port/path).
  • StatusCode – HTTP status code associated with the error.
  • Type – short descriptor (CLR exception name for API exceptions; "InternalServerError" for 5xx in Public).
  • ErrorsDictionary<string, string> of field-level messages.
  • Message – human- or key-like description.

SignalR (ReceiveError)

Errors are sent to the calling client on ReceiveError.

{
    "TraceId": "a55582ab204162e66e124b0378776ab7",
    "InvocationId": "0HMVFE0A0HMVFE0A284AMHMV00HMVFE0A284AM0A284AM",
    "Instance": "SendMessage",
    "StatusCode": 400,
    "Errors": {
        "email": "email_address_is_not_in_a_valid_format",
        "password": "password_must_be_at_least_8_characters_long"
    },
    "Message": "the_request_was_invalid_or_cannot_be_otherwise_served."
}

Caller contract

  • Include a non-empty InvocationId in hub calls (see HubArgument<T>).
  • The same InvocationId is echoed in errors to correlate requests/responses.

Naming Conventions

Use NamingConvention to transform messages (Message and Errors values).
Recommendation: ToSnakeCase or ToUpperSnakeCase.

public enum NamingConvention
{
Default = 0,
ToSnakeCase = 1,
ToPascalCase = 2,
ToCamelCase = 3,
ToKebabCase = 4,
ToTitleCase = 5,
ToHumanCase = 6,
ToUpperSnakeCase = 7
}

Custom Exceptions

Extend the base ApiException (package type) to create business-specific errors that serialize consistently.

Predefined exceptions:

  • BadRequestException
  • UnauthorizedException
  • PaymentRequiredException
  • ForbiddenException
  • NotFoundException
  • ConflictException
  • TooManyRequestsException
  • InternalServerErrorException
  • ServiceUnavailableException

Example: create a custom domain exception

using ResponseCrafter.HttpExceptions;

public sealed class OrderLimitExceededException : ApiException
{
public OrderLimitExceededException(string? message = null)
: base(statusCode: 400, message ?? "order_limit_exceeded")
{
Errors = new() { ["limit"] = "maximum_daily_order_limit_reached" };
}
}

Usage in an endpoint

app.MapPost("/orders", (CreateOrderRequest req) =>
{
if (req.Quantity > 100)
throw new OrderLimitExceededException();

// normal work...
return Results.Accepted();
});

Helper Methods (ThrowIf…)

Use the built-in helper methods to reduce guard boilerplate.

decimal? price = -10.5m;
// 400 Bad Request
BadRequestException.ThrowIfNullOrNegative(price, "price_is_negative");
// 500 Internal Server Error
InternalServerErrorException.ThrowIfNullOrNegative(price, "price_is_negative");

string? username = "   ";
// 400 Bad Request
BadRequestException.ThrowIfNullOrWhiteSpace(username, "please_provide_username");
// 404 Not Found
NotFoundException.ThrowIfNullOrWhiteSpace(username);
// 500 Internal Server Error
InternalServerErrorException.ThrowIfNullOrWhiteSpace(username, "username_required");

List<int> tags = [];
// 400 Bad Request
BadRequestException.ThrowIfNullOrEmpty(tags, "please_provide_tags");
// 404 Not Found
NotFoundException.ThrowIfNullOrEmpty(tags);
// 500 Internal Server Error
InternalServerErrorException.ThrowIfNullOrEmpty(tags, "tags_required");

object? user = null;
// 400 Bad Request
BadRequestException.ThrowIfNull(user, "please_provide_user");
// 404 Not Found
NotFoundException.ThrowIfNull(user, "user_not_found");
// 500 Internal Server Error
InternalServerErrorException.ThrowIfNull(user, "user_required");

bool userUnauthorized = false;
// 401 Unauthorized
UnauthorizedException.ThrowIf(userUnauthorized, "user_is_unauthorized");
// 500 Internal Server Error
InternalServerErrorException.ThrowIf(userUnauthorized, "authorization_check_failed");

SignalR Integration

Register the filter

builder.Services.AddSignalR(options => options.AddFilter<SignalRExceptionFilter>());

Define the hub argument contract

public interface IHubArgument
{
    string InvocationId { get; set; }
}

public class HubArgument<T> : IHubArgument
{
    public required string InvocationId { get; set; }
    public required T Argument { get; set; }
}

Hub method example

public class ChatHub : Hub
{
public async Task SendMessage(HubArgument<Message> hubArgument)
{
// Example: intentionally throwing to demonstrate an error path
throw new BadRequestException("invalid_message_format");

    // Normally:
    // await Clients.All.SendAsync("ReceiveMessage", hubArgument.Argument);

}
}

public class Message : IHubArgument
{
    public required string Message {get; set;}
    public required string InvocationId { get; set; } 
}

Client expectation

  • On error, server sends ReceiveError to the caller with the structure shown above.
  • InvocationId in the request is echoed back in the error.
  • Multi-argument hub methods are supported as long as at least one argument implements IHubArgument ( backward-compatible).

Logging & Telemetry

  • 4xx → logged as Warning
  • 5xx → logged as Error
  • Correlate using RequestId (REST) / InvocationId (SignalR) and TraceId across services.
  • The package emits enough context for centralized log aggregation systems.

Tip: You can create logging scopes around TraceId/RequestId in your app if desired.


Built-in Mappings & Behavior

  • DbUpdateConcurrencyException409 Conflict
  • BadHttpRequestException400 Bad Request with a stable “invalid payload” style message
  • GridifyException / GridifyMapperException400 Bad Request with normalized messages

Unhandled exceptions

  • Public → 5xx concealed behind a generic message + Type = "InternalServerError".
  • Private → verbose error details are returned to aid debugging.

Configuration Summary

Visibility

{
    "ResponseCrafterVisibility": "Public"
}
  • Switch to Private for internal environments to expose verbose details (do not enable in production).

Naming Convention (optional)

  • In program.cs, pass your preferred convention:
builder.AddResponseCrafter(NamingConvention.ToSnakeCase);

Exception handler log suppression (optional)

Enabled by default to avoid duplicate request-error logs from ASP.NET Core’s ExceptionHandlerMiddleware. This does not affect hosted service logs or other Microsoft.* categories.

// Keep the framework error log (opt out of suppression)
builder.AddResponseCrafter(
    namingConvention: NamingConvention.ToSnakeCase,
    suppressExceptionHandlerMiddlewareLog: false);


Frontend Integration Guidance

  • Treat Message and Errors values as localization keys.
  • Use StatusCode for UX-level branching; avoid coupling to Type names.
  • Show RequestId / TraceId in error overlays to speed up support.
  • For SignalR, correlate using InvocationId.

Versioning & Compatibility

  • No breaking changes to existing fields without a major version bump.
  • We may add optional fields in minor versions—clients should ignore unknown fields.
  • The SignalR error event name remains ReceiveError.

Limitations

  • Designed for .NET 8+.

License

MIT


Product Compatible and additional computed target framework versions.
.NET 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 was computed.  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

Pandatech.SharedKernel provides centralized configurations, utilities, and extensions for ASP.NET Core projects. For more information refere to readme.md document.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
5.2.0 94 8/15/2025
5.1.12 204 8/7/2025
5.1.11 106 7/30/2025
5.1.10 209 6/1/2025
5.1.9 211 4/9/2025
5.1.8 188 2/18/2025
5.1.7 151 2/17/2025
5.1.6 148 2/5/2025
5.1.5 140 1/10/2025
5.1.4 97 1/9/2025
5.1.3 145 12/18/2024
5.1.2 137 12/17/2024
5.1.1 162 12/13/2024
5.1.0 131 12/11/2024
5.0.3 147 11/28/2024
5.0.2 163 11/23/2024
5.0.1 160 11/21/2024
5.0.0 128 11/21/2024
4.0.1 154 11/11/2024
4.0.0 155 10/4/2024
3.0.1 195 9/11/2024
3.0.0 157 7/18/2024
2.2.2 159 6/29/2024
2.2.1 156 6/29/2024
2.2.0 160 6/27/2024
2.1.4 164 6/27/2024
2.1.3 194 6/22/2024
2.1.2 133 6/22/2024
2.1.1 146 6/22/2024
2.1.0 142 6/22/2024
2.0.0 154 6/13/2024
1.7.0 137 6/13/2024
1.6.3 145 6/9/2024
1.6.2 149 6/6/2024
1.6.1 168 6/2/2024
1.5.1 155 5/28/2024
1.5.0 154 5/26/2024
1.4.14 160 5/24/2024
1.4.13 142 5/24/2024
1.4.12 157 5/17/2024
1.4.10 167 5/8/2024
1.4.9 173 5/7/2024
1.4.8 151 5/6/2024
1.4.7 164 5/5/2024
1.4.6 128 5/3/2024
1.4.5 159 4/24/2024
1.4.4 152 4/24/2024
1.4.3 150 4/24/2024
1.4.2 149 4/23/2024
1.4.1 169 4/16/2024
1.4.0 175 4/16/2024
1.3.0 148 4/16/2024
1.2.1 180 4/3/2024
1.2.0 185 4/2/2024
1.1.0 215 3/22/2024
1.0.4 513 12/14/2023
1.0.3 196 11/29/2023
1.0.2 183 11/29/2023
1.0.1 188 11/28/2023
1.0.0 199 11/28/2023

Quality/DX polish (no breaking changes): SignalR InvocationId scanning (multi-arg); structured logging scopes; optional suppression of framework duplicate error log (default on); JSON error mapping via JsonException.Path; consistent TraceId; normalized Errors key/value casing; docs/demo updates.