Pandatech.ResponseCrafter
7.0.0
dotnet add package Pandatech.ResponseCrafter --version 7.0.0
NuGet\Install-Package Pandatech.ResponseCrafter -Version 7.0.0
<PackageReference Include="Pandatech.ResponseCrafter" Version="7.0.0" />
<PackageVersion Include="Pandatech.ResponseCrafter" Version="7.0.0" />
<PackageReference Include="Pandatech.ResponseCrafter" />
paket add Pandatech.ResponseCrafter --version 7.0.0
#r "nuget: Pandatech.ResponseCrafter, 7.0.0"
#:package Pandatech.ResponseCrafter@7.0.0
#addin nuget:?package=Pandatech.ResponseCrafter&version=7.0.0
#tool nuget:?package=Pandatech.ResponseCrafter&version=7.0.0
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 transformationToSnakeCase-user_not_foundToCamelCase-userNotFoundToPascalCase-UserNotFoundToKebabCase-user-not-foundToTitleCase-User Not FoundToHumanCase-User not foundToUpperSnakeCase-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
- 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()- 400ProducesUnauthorized()- 401ProducesForbidden()- 403ProducesNotFound()- 404ProducesConflict()- 409ProducesTooManyRequests()- 429ProducesServiceUnavailable()- 503ProducesPaymentRequired()- 402ProducesErrorResponse(statusCode)- Custom statusProducesErrorResponse(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 | Versions 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. |
-
net10.0
- Humanizer (>= 3.0.1)
- PandaTech.FluentImporter (>= 5.0.0)
- Pandatech.GridifyExtensions (>= 4.0.0)
-
net8.0
- Humanizer (>= 3.0.1)
- PandaTech.FluentImporter (>= 5.0.0)
- Pandatech.GridifyExtensions (>= 4.0.0)
-
net9.0
- Humanizer (>= 3.0.1)
- PandaTech.FluentImporter (>= 5.0.0)
- Pandatech.GridifyExtensions (>= 4.0.0)
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.0.0 | 34 | 2/28/2026 |
| 6.0.2 | 53 | 2/26/2026 |
| 6.0.1 | 126 | 1/26/2026 |
| 6.0.0 | 106 | 12/28/2025 |
| 5.3.1 | 179 | 12/23/2025 |
| 5.3.0 | 257 | 10/30/2025 |
| 5.2.2 | 257 | 9/8/2025 |
| 5.2.1 | 202 | 9/8/2025 |
| 5.2.0 | 193 | 8/15/2025 |
| 5.1.12 | 284 | 8/7/2025 |
| 5.1.11 | 181 | 7/30/2025 |
| 5.1.10 | 315 | 6/1/2025 |
| 5.1.9 | 294 | 4/9/2025 |
| 5.1.8 | 265 | 2/18/2025 |
| 5.1.7 | 217 | 2/17/2025 |
| 5.1.6 | 215 | 2/5/2025 |
| 5.1.5 | 224 | 1/10/2025 |
| 5.1.4 | 180 | 1/9/2025 |
| 5.1.3 | 224 | 12/18/2024 |
| 5.1.2 | 220 | 12/17/2024 |
Multi-target net8.0/9.0/10.0, source-generated logging, path-only instance URIs, updated dependencies, fixed extension method syntax