BonyadCode.Resulter.AspNetCore
1.1.2
dotnet add package BonyadCode.Resulter.AspNetCore --version 1.1.2
NuGet\Install-Package BonyadCode.Resulter.AspNetCore -Version 1.1.2
<PackageReference Include="BonyadCode.Resulter.AspNetCore" Version="1.1.2" />
<PackageVersion Include="BonyadCode.Resulter.AspNetCore" Version="1.1.2" />
<PackageReference Include="BonyadCode.Resulter.AspNetCore" />
paket add BonyadCode.Resulter.AspNetCore --version 1.1.2
#r "nuget: BonyadCode.Resulter.AspNetCore, 1.1.2"
#addin nuget:?package=BonyadCode.Resulter.AspNetCore&version=1.1.2
#tool nuget:?package=BonyadCode.Resulter.AspNetCore&version=1.1.2
In the name of God
BonyadCode.Resulter.AspNetCore
A utility library to standardize API responses in ASP.NET Core. It supports both Controllers and Minimal APIs with consistent handling of success, failure, validation errors, and exceptions, using rich ProblemDetails following RFC7807.
โจ Features
- Unified Result Model: Consistent
ResultBuilder<T>
andResultBuilder
for all HTTP outcomes. - Support for Minimal APIs & Controllers: Seamlessly convert to
IActionResult
orIResult
. - ProblemDetails Integration: Built-in helpers to enrich error responses with metadata.
- Zero Boilerplate: Clean, expressive syntax that reduces repetitive response code.
- Validation: and Error Support: Maps errors from FluentValidation, DataAnnotations, and IdentityResult into structured responses.
๐ฆ Installation
dotnet add package BonyadCode.Resulter.AspNetCore
๐ Quick Examples
โ Success (Controller)
[HttpGet("hello")]
public IActionResult GetHello()
{
var result = ResultBuilder<string>.Success("Hello, world!");
return result.ToHttpResultController();
}
Response JSON:
{
"succeeded": true,
"statusCode": 200,
"data": "Hello, world!",
"problemDetails": null
}
โ Success (Minimal Api)
app.MapGet("/hello", () =>
{
var result = ResultBuilder<string>.Success("Hello, world!");
return result.ToHttpResultMinimal();
});
Response JSON:
{
"succeeded": true,
"statusCode": 200,
"data": "Hello, world!",
"problemDetails": null
}
โ Custom Success (Controller)
[HttpGet("hello")]
public IActionResult GetHello()
{
var result = ResultBuilder.Success("User was Created", HttpStatusCode.Created);
return result.ToHttpResultController();
}
Response JSON:
{
"succeeded": true,
"statusCode": 201,
"data": "User was Created",
"problemDetails": null
}
โ Custom Success (Minimal Api)
app.MapGet("/hello", () =>
{
var result = ResultBuilder.Success("User was Created", HttpStatusCode.Created);
return result.ToHttpResultMinimal();
});
Response JSON:
{
"succeeded": true,
"statusCode": 201,
"data": "User was Created",
"problemDetails": null
}
โ Validation Failure (Controller)
[HttpPost("register")]
public IActionResult RegisterUser(UserDto dto)
{
var result = ResultBuilder<string>.Failure()
.AddProblemDetailsFromKeyValuePairs("Email", "Email is required.")
.AddProblemDetailsFromKeyValuePairs("Password", "Password must be at least 8 characters." );
return result.ToHttpResultController();
}
or
[HttpPost("register")]
public IActionResult RegisterUser(UserDto dto)
{
var result = ResultBuilder<string>.Failure()
.AddProblemDetailsFromKeyValuePairs(["Email","Password"], ["Email is required.","Password must be at least 8 characters." ]);
return result.ToHttpResultController();
}
Response JSON:
{
"succeeded": false,
"statusCode": 400,
"data": null,
"problemDetails": {
"type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
"title": "A problem occurred.",
"detail": "A problem occurred (400 BadRequest).",
"status": 400,
"extensions": {
"Email": [
"Email is required."
],
"Password": [
"Password must be at least 8 characters."
]
}
}
}
๐ Minimal API Usage
โ Success
app.MapGet("/status", () =>
{
var result = ResultBuilder<string>.Success("All systems operational.", HttpStatusCode.NoContent);
return result.ToHttpResultMinimal();
});
or
app.MapGet("/status", () =>
{
var result = ResultBuilder<string>.Success("All systems operational.");
return result.ToHttpResultMinimal(HttpStatusCode.NoContent);
});
Response JSON:
{
"succeeded": true,
"statusCode": 204,
"data": "All systems operational.",
"problemDetails": null
}
โ FluentValidation Errors
app.MapPost("/login", (LoginRequest request, HttpContext? httpContext, IValidator<LoginRequest> validator) =>
{
var validationResult = validator.Validate(request);
if(!validationResult.Succeeded)
{
var result = ResultBuilder<string>.Failure()
.AddProblemDetailsFromFluentValidationResult(validationResult);
return result.ToHttpResultMinimal(HttpStatusCode.Conflict, httpContext);
}
return ResultBuilder<string>.Success("Logged in").ToHttpResultMinimal();
});
Response JSON:
{
"succeeded": false,
"statusCode": 409,
"data": null,
"problemDetails": {
"type": "https://example.com/problems/validation",
"title": "A validation problem occurred.",
"detail": "One or more validation failures occured.",
"status": 409,
"instance": "/api/register",
"extensions": {
"Username": [
"Username already exists."
]
}
}
}
โ๏ธ Custom ProblemDetails Examples
๐ ๏ธ Attach Custom Metadata
var result = ResultBuilder<string>.Failure()
.AddCustomProblemDetails(
type: "https://example.com/problems/validation",
title: "A custom problem occurred.",
details: "One or more custom failures occured.",
statusCode: HttpStatusCode.Conflict,
instance: "/api/register",
extensions: new Dictionary<string, object?>
{
{ "Username", new[] { "Username already exists." } }
}).ToHttpResultMinimal(HttpStatusCode.Conflict, httpContext);
Note: You can pass the httpContext to the above method so that "instance" is extracted automatically from it.
Response JSON:
{
"succeeded": false,
"statusCode": 409,
"data": null,
"problemDetails": {
"type": "https://example.com/problems/validation",
"title": "A custom problem occurred.",
"detail": "One or more custom failures occured.",
"status": 409,
"instance": "/api/register",
"extensions": {
"Username": [
"Username already exists."
]
}
}
}
๐ง Validation Sources
๐ง DataAnnotations
var validationResult = new ValidationResult("Email", new[] { "Invalid email" });
var result = ResultBuilder.Failure()
.AddProblemDetailsFromValidationResult(validationResult);
๐งช FluentValidation
var result = ResultBuilder.Failure()
.AddProblemDetailsFromFluentValidationResult(validationResult);
๐ IdentityResult
var result = ResultBuilder.Failure()
.AddProblemDetailsFromIdentityError(identityResult);
๐ฅ Exception Handling
try
{
throw new InvalidOperationException("Something broke.");
}
catch (Exception ex)
{
var result = ResultBuilder.Failure()
.AddProblemDetailsFromException(ex, httpContext);
return result.ToHttpResultController();
}
Response JSON (truncated):
{
"succeeded": false,
"statusCode": 500,
"data": null,
"problemDetails": {
"type": "https://tools.ietf.org/html/rfc7231#section-6.6.1",
"title": "A Server problem occurred.",
"detail": {
"message": "Something broke."
},
"status": 500,
"instance": "/api/failing-endpoint",
"extensions": {
"Message": [
"Something broke."
],
"Source": [
"MyApp"
]
}
}
}
๐ Extension Reference
Endpoint Result Methods:
Method | Description |
---|---|
ToHttpResultController() |
returns an IActionResult result for use in Controllers |
ToHttpResultController(...) |
returns an IActionResult result for use in Controllers (accepting parameters) |
ToHttpResultMinimal() |
returns an IResult result for use in Minimal |
ToHttpResultMinimal(...) |
returns an IResult result for use in Minimal APIs (accepting parameters) |
ProblemDetails Helper Methods:
Method | Description |
---|---|
AddSimpleProblemDetails() |
Attaches default problem info |
AddCustomProblemDetails(...) |
Fully customized metadata |
AddProblemDetailsFromException(...) |
Adds public exception properties |
AddProblemDetailsFromIdentityError(...) |
Maps IdentityResult errors |
AddProblemDetailsFromFluentValidationResult(...) |
Maps FluentValidation errors |
AddProblemDetailsFromValidationResult(...) |
Maps DataAnnotation errors |
AddProblemDetailsFromKeyValuePairs(...) |
Adds custom errors by key/value(s) |
๐งช API Behavior Matrix
Scenario | Method | Status Code | Description |
---|---|---|---|
Success | ResultBuilder.Success() |
Any (Default: 200 OK) | Standard success with optional data |
Failure | ResultBuilder.Failure() |
Any (Default: 400 Bad Request) | Standard failure with optional data (exception, validation, etc.) |
Create | ResultBuilder.Create() |
Any | User-defined structured error response |
๐ค Contributing
PRs and feedback welcome! GitHub โ
๐ License
Apache 2.0 โ see the LICENSE file.
๐ฆ Links
Usage Conditions:
- This program must not be used for any military or governmental purposes without the owner's consent.
- This program must not be used for any inhumane purposes.
- This program must not be used for any illegal or haram activities.
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 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. |
-
net8.0
- FluentValidation (>= 12.0.0)
- Newtonsoft.Json (>= 13.0.3)
-
net9.0
- FluentValidation (>= 12.0.0)
- Newtonsoft.Json (>= 13.0.3)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.