QERP.Response
1.5.2
dotnet add package QERP.Response --version 1.5.2
NuGet\Install-Package QERP.Response -Version 1.5.2
<PackageReference Include="QERP.Response" Version="1.5.2" />
<PackageVersion Include="QERP.Response" Version="1.5.2" />
<PackageReference Include="QERP.Response" />
paket add QERP.Response --version 1.5.2
#r "nuget: QERP.Response, 1.5.2"
#:package QERP.Response@1.5.2
#addin nuget:?package=QERP.Response&version=1.5.2
#tool nuget:?package=QERP.Response&version=1.5.2
Qerp.QERP.Response
Standardized API response package for ASP.NET Core with built-in global exception handler middleware and message management. Provides consistent response models, typed exceptions, W3C TraceId/SpanId correlation, application-level response codes, EF Core message lookup, optional Redis caching, and CRUD operations for response messages.
Features
- Fluent builder pattern -- configure with
AddStructuredResponse(api => api.UsePostgreSql(...).UseRedis(...).UseMessageManagement(...)) - Simple response model --
ApiResponse<T>generic wrapper for any data type with aStatusflag. - Global exception handler -- middleware catches all unhandled exceptions and returns standardized error responses
- Built-in exception types --
NotFoundException,BadRequestException,ValidationException,UnauthorizedException,ForbiddenException,ConflictException, andServiceUnavailableException - Message management -- opt-in CRUD service for response messages (Seed, Add, Edit, Activate, Deactivate, List)
- W3C Trace Context -- automatic TraceId and SpanId from OpenTelemetry activities
- Application response codes -- categorized enum (
MessageCode) with ranges for Success, Client Errors, Auth Errors, and Server Errors - Database-driven messages -- response messages stored in PostgreSQL via EF Core
- Optional Redis caching -- cache-aside pattern with automatic invalidation on mutations
- Read/write DB separation -- read connection for queries, write connection for management
- Auto-seed -- database is automatically seeded with default messages when message management is enabled
- Audit trail -- all entities inherit
AuditEntitywithCreatedBy,CreatedAt,UpdatedBy,UpdatedAt - Multi-target -- supports net8.0, net9.0, and net10.0
Installation
dotnet add package QERP.Response
Quick Start
1. Register Services and Middleware
using QERP.Response.Extensions;
// Minimal (read-only DB, no Redis, no management)
builder.Services.AddStructuredResponse(api => api
.UsePostgreSql("Host=localhost;Database=qerp;Username=postgres;Password=secret")
);
// Full (management + auto-seed + Redis)
builder.Services.AddStructuredResponse(api => api
.UsePostgreSql("Host=read-replica;Database=qerp;...")
.UseRedis("localhost:6379")
.UseMessageManagement("Host=primary;Database=qerp;...")
);
var app = builder.Build();
app.UseStructuredResponse(); // registers exception handler and request logging
2. Use the Response Factory
using QERP.Response.Builders;
using QERP.Response.Enums;
using QERP.Response.Exceptions;
// --- Example 1: Success with default code (0 - Success) ---
app.MapGet("/api/products/{id}", async (int id, ResponseFactory response) =>
{
var product = await productService.GetByIdAsync(id);
if (product is null) throw new NotFoundException();
var result = await response.SuccessAsync(product);
return Results.Ok(result);
});
// --- Example 2: Success with custom code (1 - Created) ---
app.MapPost("/api/products", async (Product product, ResponseFactory response) =>
{
await productService.AddAsync(product);
var result = await response.SuccessAsync(product, MessageCode.Created);
return Results.Json(result, statusCode: 201);
});
// --- Example 3: Manual failure with custom errors ---
app.MapPost("/api/validate", async (ValidateRequest req, ResponseFactory response) =>
{
if (string.IsNullOrEmpty(req.Name))
{
var errors = new List<string> { "Name is required for validation." };
var result = await response.FailAsync<object>(MessageCode.ValidationError, errors);
return Results.BadRequest(result);
}
return Results.Ok(await response.SuccessAsync(new { Valid = true }));
});
3. Response Output
Success:
{
"status": true,
"code": 0,
"traceId": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
"spanId": "1a2b3c4d5e6f7a8b",
"timestamp": "2026-03-03T14:10:00Z",
"message": "Operation completed successfully.",
"data": {
"id": 1,
"name": "Laptop",
"price": 1299.99
}
}
Exception (handled by middleware):
{
"status": false,
"code": 102,
"traceId": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
"spanId": "1a2b3c4d5e6f7a8b",
"timestamp": "2026-03-03T14:10:05Z",
"message": "The requested resource was not found.",
"errors": ["Product 123 was not found."],
"data": null
}
Builder Configuration
| Method | Required | Description |
|---|---|---|
.UsePostgreSql(readConn) |
Yes | Read-only DB connection for message queries. |
.UseRedis(conn) |
No | Redis connection for caching. Without it, queries hit DB directly. |
.UseMessageManagement(writeConn) |
No | Enables CRUD management service + auto-seed. Requires write-capable DB connection. |
Factory Methods
Inject ResponseFactory to create responses manually:
await response.SuccessAsync(data);
await response.SuccessAsync(data, MessageCode.Created);
await response.FailAsync<Product>(MessageCode.NotFound);
await response.FailAsync<Product>(MessageCode.NotFound, errors);
Message Management Service
Available when .UseMessageManagement(writeConn) is configured. Inject IApplicationMessageManagementService:
using QERP.Response.Services;
using QERP.Response.Models;
app.MapGet("/api/messages", async (IApplicationMessageManagementService mgmt) =>
{
var messages = await mgmt.ListAsync();
return Results.Ok(messages);
});
app.MapPost("/api/messages", async (MessageRequest req, IApplicationMessageManagementService mgmt) =>
{
var created = await mgmt.AddAsync(req);
return Results.Created($"/api/messages/{created.Id}", created);
});
| Method | Description |
|---|---|
SeedAsync() |
Seeds default messages for all response codes (skips if already seeded) |
AddAsync(request) |
Creates a new response message |
EditAsync(id, request) |
Updates an existing message by ID |
ActivateAsync(id) |
Sets IsActive = true |
DeactivateAsync(id) |
Sets IsActive = false |
ListAsync() |
Returns all messages ordered by message code |
Built-in Exception Types
Throw these anywhere in your code -- the middleware catches them and returns the correct HTTP status with a standardized ApiResponse model:
| Exception | HTTP Status | Default Mapping (MessageCode) |
|---|---|---|
BadRequestException |
400 | BadRequest (100) |
UnauthorizedException |
401 | AuthenticationFailed (200) |
ForbiddenException |
403 | AuthorizationFailed (201) |
NotFoundException |
404 | NotFound (102) |
ConflictException |
409 | Conflict (103) |
ValidationException |
422 | ValidationError (101) |
ServiceUnavailableException |
503 | ServiceUnavailable (301) |
| Any other exception | 500 | InternalError (300) |
Custom Exceptions
You can throw BaseException directly for custom HTTP status codes:
throw new BaseException("Rate limit exceeded", HttpStatusCode.TooManyRequests);
Message Codes (MessageCode)
| Range | Category | Examples |
|---|---|---|
| 0-99 | Success | Success(0), Created(1), Updated(2), Deleted(3) |
| 100-199 | Client Error | BadRequest(100), ValidationError(101), NotFound(102) |
| 200-299 | Auth Error | AuthenticationFailed(200), AuthorizationFailed(201) |
| 300-399 | Server Error | InternalError(300), ServiceUnavailable(301) |
Detailed Demo
For a full demo application, see the tests/response-pkg.app project. It includes:
- Program.cs: Full configuration with PostgreSQL, Redis, and Logging.
- ProductsController: Real-world CRUD examples using
ResponseFactory. - TestController: Demonstration of all built-in exception types.
- MessagesController: CRUD operations for managing response messages in the database.
Running the Demo
- Configure the connection strings in
appsettings.json. - Run the project:
dotnet run --project tests/response-pkg.app. - Use the response-pkg.app.http file to test endpoints.
License
This project is licensed under the MIT License. See the LICENSE file for details.
| 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
- Mapster (>= 7.4.0)
- Microsoft.EntityFrameworkCore (>= 10.0.3)
- Npgsql.EntityFrameworkCore.PostgreSQL (>= 10.0.0)
- QERP.Log (>= 1.1.0)
- StackExchange.Redis (>= 2.8.24)
-
net8.0
- Mapster (>= 7.4.0)
- Microsoft.EntityFrameworkCore (>= 8.0.12)
- Npgsql.EntityFrameworkCore.PostgreSQL (>= 8.0.11)
- QERP.Log (>= 1.1.0)
- StackExchange.Redis (>= 2.8.24)
-
net9.0
- Mapster (>= 7.4.0)
- Microsoft.EntityFrameworkCore (>= 9.0.3)
- Npgsql.EntityFrameworkCore.PostgreSQL (>= 9.0.4)
- QERP.Log (>= 1.1.0)
- StackExchange.Redis (>= 2.8.24)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.