RA.Utilities.Api.Response
RA.Utilities.Api.Response provides a set of ready-made response models for ASP.NET Core APIs. Every model shares the same envelope — responseCode, responseType, responseMessage, and result — so clients can handle success and failure uniformly.
Use it when you want consistent JSON contracts across endpoints without hand-building response objects, or when you need typed error payloads such as NotFoundResult and BadRequestResult.
🎯 Purpose
Hand-built API responses drift: one endpoint returns { "error": "..." }, another returns a bare string, and a third uses a different status field. This package replaces that with a single envelope and a family of pre-configured response types. It helps you:
- Standardize the contract: Every response has the same four top-level properties.
- Reduce boilerplate:
SuccessResponse<T>and the error responses set the status code, semantic type, and default message for you. - Keep errors typed:
NotFoundResult,ConflictResult, andBadRequestResultcarry structured domain context instead of a plain string. - Integrate with the ecosystem: Works with
RA.Utilities.Apimiddleware, exception mapping, and the constants inRA.Utilities.Core.Constants.
✨ Key Features
- One envelope —
Response<T>definesResponseCode,ResponseType,ResponseMessage, andResult - 12 pre-configured responses — success, 400, 401, 402, 403, 404, 409, 422, 429, 500, 503, and 504
- Typed error payloads —
BadRequestResult,NotFoundResult,ConflictResult, andErrorResult - Convenience constructors — single-argument constructors apply the standard code and message
- Custom overloads — override the response code and message when a domain needs it
- Immutable after construction — all
Response<T>properties useinitaccessors - Zero configuration — no DI registration, no options, no setup
v10.1.0 changes the namespace from RA.Utilities.Api.Results to RA.Utilities.Api.Response, redesigns the constructor overloads, and — through RA.Utilities.Core.Constants v10.0.4 — writes responseType as a number by default. v10.2.0 adds PaymentRequiredResponse (HTTP 402) and raises the RA.Utilities.Core.Constants dependency to 10.1.1. See the Migration Guides.
🛠️ Getting Started
Prerequisites
- .NET 10.0 or later
Installation
.NET CLI
dotnet add package RA.Utilities.Api.Response
Package Manager Console
Install-Package RA.Utilities.Api.Response
PackageReference (csproj)
<PackageReference Include="RA.Utilities.Api.Response" Version="10.2.0" />
Hello World
using Microsoft.AspNetCore.Mvc;
using RA.Utilities.Api.Response;
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
[HttpGet("{id}")]
public IActionResult GetProduct(int id)
{
var product = _productService.GetById(id);
if (product is null)
{
// 404 with a structured NotFoundResult payload
return NotFound(new NotFoundResponse(new NotFoundResult("Product", id)));
}
// 200 with the product as the result payload
return Ok(new SuccessResponse<Product>(product));
}
}
No DI registration or configuration is required. Add the using directive and return the model from your action.
🧠 Core Concepts
| Type | HTTP | ResponseType | Result payload |
|---|---|---|---|
Response<T> | any | any | T |
SuccessResponse<T> | 200 | Success (JSON 0) | T |
BadRequestResponse | 400 | BadRequest (JSON 12) | BadRequestResult[] |
UnauthorizedResponse | 401 | Unauthorized (JSON 10) | ErrorResult |
PaymentRequiredResponse | 402 | PaymentRequired (JSON 18) | ErrorResult |
ForbiddenResponse | 403 | Forbidden (JSON 14) | ErrorResult |
NotFoundResponse | 404 | NotFound (JSON 8) | NotFoundResult |
ConflictResponse | 409 | Conflict (JSON 9) | ConflictResult |
UnprocessableResponse | 422 | Unprocessable (JSON 13) | ErrorResult |
TooManyRequestsResponse | 429 | TooManyRequests (JSON 15) | ErrorResult |
ErrorResponse | 500 | Error (JSON 11) | ErrorResult |
ServiceUnavailableResponse | 503 | ServiceUnavailable (JSON 16) | ErrorResult |
GatewayTimeoutResponse | 504 | GatewayTimeout (JSON 17) | ErrorResult |
How they work together
Every response class derives from Response<T> and fills the envelope in its constructor. The controller returns the model (usually through Ok, BadRequest, NotFound, Conflict, or StatusCode), and ASP.NET Core serializes it.
new NotFoundResponse(new NotFoundResult("Product", 99))
├─ ResponseCode = 404
├─ ResponseType = ResponseType.NotFound → JSON: 8
├─ ResponseMessage = "The requested resource was not found."
└─ Result = NotFoundResult
├─ Entity = "Product"
├─ Value = 99
├─ ErrorCode = "NotFound"
└─ ErrorMessage = "" (set explicitly to override)
Terminology
- Envelope: The four properties inherited from
Response<T>that wrap every payload. - Result payload: The
Resultproperty — a success object, aBadRequestResult[], or anErrorResultsubtype. - Convenience constructor: A single-argument constructor that applies the standard HTTP code, semantic type, and default message.
- Custom overload: A constructor that takes an explicit
responseCodeandresponseMessagefor domain-specific overrides. - Numeric
responseType:ResponseTypeis an enum. With defaultSystem.Text.Jsonoptions it is written as a number; registerJsonStringEnumConverterto write names such as"NotFound".
📖 How-To Guides
Return a successful response
using Microsoft.AspNetCore.Mvc;
using RA.Utilities.Api.Response;
[HttpGet("{id}")]
public IActionResult GetProduct(int id)
{
var product = _productService.GetById(id);
if (product is null)
{
return NotFound(new NotFoundResponse(new NotFoundResult("Product", id)));
}
return Ok(new SuccessResponse<Product>(product));
}
Return a success with no payload
SuccessResponse<T> has a parameterless constructor that leaves Result at default:
return Ok(new SuccessResponse<Product>());
For a 204 No Content, prefer ASP.NET Core's NoContent() — this package has no dedicated no-content model.
Return a 400 with validation errors
var errors = new[]
{
new BadRequestResult
{
PropertyName = "Email",
ErrorMessage = "'Email' must not be empty.",
AttemptedValue = request.Email,
ErrorCode = "NotEmptyValidator"
},
new BadRequestResult
{
PropertyName = "Age",
ErrorMessage = "'Age' must be 18 or older.",
AttemptedValue = request.Age,
ErrorCode = "AGE_TOO_LOW"
}
};
return BadRequest(new BadRequestResponse(errors));
BadRequestResult is the element type; BadRequestResponse is the envelope whose Result is BadRequestResult[].
Return a 404 with entity details
return NotFound(new NotFoundResponse(new NotFoundResult("Product", id)));
NotFoundResult defaults its ErrorCode to "NotFound" and its ErrorMessage to an empty string. Pass the optional message argument when you want the nested payload to carry text:
var result = new NotFoundResult(
"Product",
id,
errorCode: "PRODUCT_NOT_FOUND",
message: $"Product with id {id} was not found.");
return NotFound(new NotFoundResponse(result));
Return a 409 conflict
var conflict = new ConflictResult("User", request.Email)
{
ErrorMessage = $"A user with email '{request.Email}' already exists."
};
return Conflict(new ConflictResponse(conflict));
ConflictResult defaults ErrorCode to null (omitted from JSON) and ErrorMessage to an empty string.
Return a 402 when payment is required
return StatusCode(402, new PaymentRequiredResponse());
The parameterless constructor sets ResponseCode = 402, ResponseType = ResponseType.PaymentRequired, ResponseMessage = "Payment is required to access this resource.", and leaves Result null. Pass a code and message when the reason is domain-specific:
return StatusCode(402, new PaymentRequiredResponse(
402,
"This report is available on paid plans."));
Return a 500 without leaking details
return StatusCode(500, new ErrorResponse());
The parameterless constructor sets ResponseCode = 500, ResponseType = ResponseType.Error, ResponseMessage = "Something happened on our end.", and leaves Result null.
Override the code and message
Every error response has an all-argument overload. BadRequestResponse and ConflictResponse require both values; the others accept a nullable code and an optional message:
// BadRequestResponse requires responseCode AND responseMessage
return BadRequest(new BadRequestResponse(
validationError,
responseCode: 422,
responseMessage: "The request could not be processed."));
// NotFoundResponse accepts a nullable code and optional message
return NotFound(new NotFoundResponse(
new NotFoundResult("Product", id),
responseCode: null,
responseMessage: "Product not found."));
Empty-message default: On the all-argument overloads the
responseMessageparameter defaults to"", not to the standard message. If you omit it,ResponseMessageis empty. Use the convenience constructor when you want the standard text.
Serialize ResponseType as a string
The models contain a ResponseType enum. By default, System.Text.Json writes its numeric value (8, 12, ...). Register JsonStringEnumConverter if your clients expect names:
using System.Text.Json.Serialization;
builder.Services.ConfigureHttpJsonOptions(options =>
{
options.SerializerOptions.Converters.Add(new JsonStringEnumConverter());
});
Create a custom response type
Derive from Response<T> when you need a status code the package does not wrap. The init accessors can be set from the constructor:
using RA.Utilities.Api.Response;
using RA.Utilities.Core.Constants;
public sealed class TeapotResponse : Response<ErrorResult>
{
public TeapotResponse()
{
ResponseCode = 418;
ResponseType = ResponseType.Error;
ResponseMessage = "I'm a teapot.";
Result = new ErrorResult
{
ErrorCode = "TEAPOT",
ErrorMessage = "The server refuses to brew coffee."
};
}
}
⚙️ Configuration
This package has no configuration options. There is no DI registration, no appsettings.json entries, and no options classes.
The only optional setup is JSON serialization: register JsonStringEnumConverter if your API contract expects responseType names instead of numbers. See Serialize ResponseType as a string.
✨ Available Response Types
Response<T>
The Response class is the universal wrapper for all API responses in the RA.Utilities ecosystem. It defines the JSON envelope shared by every response model: responseCode, responseType, responseMessage, and result.
SuccessResponse<T>
The SuccessResponse class wraps a successful payload in the standard Response envelope. It sets ResponseCode = 200 (BaseResponseCode.Success), ResponseType = ResponseType.Success, and the standard success message.
BadRequestResponse
The BadRequestResponse class creates a standardized 400 Bad Request response for validation failures. It derives from Response with T = BadRequestResult[], so Result always contains one or more structured validation errors.
NotFoundResponse
The NotFoundResponse class creates a standardized 404 Not Found response. It derives from Response with T = NotFoundResult, so Result identifies the entity that was searched for and the value that was used.
ConflictResponse
The ConflictResponse class creates a standardized 409 Conflict response. It derives from Response with T = ConflictResult, so Result identifies the entity and value that caused the conflict.
ErrorResponse
The ErrorResponse class creates a standardized 500 Internal Server Error response. It derives from Response with T = ErrorResult and is the catch-all model for unexpected failures.
ForbiddenResponse
The ForbiddenResponse class creates a standardized 403 Forbidden response. It derives from Response with T = ErrorResult and is used when the caller is authenticated but lacks permission.
UnauthorizedResponse
The UnauthorizedResponse class creates a standardized 401 Unauthorized response. It derives from Response with T = ErrorResult and is used when authentication is missing or invalid.
UnprocessableResponse
The UnprocessableResponse class creates a standardized 422 Unprocessable Entity response. It derives from Response with T = ErrorResult and is used when the request is syntactically valid but semantically impossible.
TooManyRequestsResponse
The TooManyRequestsResponse class creates a standardized 429 Too Many Requests response. It derives from Response with T = ErrorResult and is used when a client exceeds a rate limit.
ServiceUnavailableResponse
The ServiceUnavailableResponse class creates a standardized 503 Service Unavailable response. It derives from Response with T = ErrorResult and is used when the service is temporarily unable to handle requests.
GatewayTimeoutResponse
The GatewayTimeoutResponse class creates a standardized 504 Gateway Timeout response. It derives from Response with T = ErrorResult and is used when an upstream service does not respond in time.
ErrorResult
The ErrorResult class is the base payload for structured errors. It is used directly by ErrorResponse, UnauthorizedResponse, PaymentRequiredResponse, ForbiddenResponse, UnprocessableResponse, TooManyRequestsResponse, ServiceUnavailableResponse, and GatewayTimeoutResponse, and it is the base class for BadRequestResult, NotFoundResult, and ConflictResult.
PaymentRequiredResponse
The PaymentRequiredResponse class creates a standardized 402 Payment Required response. It derives from Response with T = ErrorResult and is used when an authenticated caller must pay before accessing the resource.
Migration Guides
v10.1.0 → v10.2.0
Troubleshooting & FAQ
Common Issues
🙌 Contributing & Support
Found a bug or have a suggestion?
- GitHub Issues: Report a bug or request a feature
- Discussions: Ask questions or start a discussion
Contributing
Contributions are welcome! Please follow the standard workflow:
- Fork the RA.Utilities repository
- Create a branch from
mainwith a descriptive name (e.g.,feature/add-response-type) - Make your changes — add XML doc comments for new public APIs and keep the envelope shape unchanged
- Submit a pull request to the
mainbranch with a clear description
Links
| Resource | URL |
|---|---|
| NuGet | RA.Utilities.Api.Response |
| Source | GitHub — RedonAlla/RA.Utilities |
| License | MIT |
| Build Status | GitHub Actions |