UnambitiousFx.Functional.AspNetCore
1.0.6
dotnet add package UnambitiousFx.Functional.AspNetCore --version 1.0.6
NuGet\Install-Package UnambitiousFx.Functional.AspNetCore -Version 1.0.6
<PackageReference Include="UnambitiousFx.Functional.AspNetCore" Version="1.0.6" />
<PackageVersion Include="UnambitiousFx.Functional.AspNetCore" Version="1.0.6" />
<PackageReference Include="UnambitiousFx.Functional.AspNetCore" />
paket add UnambitiousFx.Functional.AspNetCore --version 1.0.6
#r "nuget: UnambitiousFx.Functional.AspNetCore, 1.0.6"
#:package UnambitiousFx.Functional.AspNetCore@1.0.6
#addin nuget:?package=UnambitiousFx.Functional.AspNetCore&version=1.0.6
#tool nuget:?package=UnambitiousFx.Functional.AspNetCore&version=1.0.6
Functional.AspNetCore
ASP.NET Core integration for UnambitiousFx Result<T> types. This library provides seamless conversion from Result types to HTTP responses for both minimal APIs and MVC controllers.
Features
- ✅ AOT-Compatible: Designed for Native AOT compilation
- ✅ Minimal API Support: Convert
Result<T>toIResult - ✅ MVC Controller Support: Convert
Result<T>toIActionResult - ✅ DTO Mapping: Optional transformation layer for response DTOs
- ✅ Extensible Error Mapping: Custom error-to-HTTP status code mappers
- ✅ Problem Details Support: RFC 7807 compliant error responses
- ✅ Built-in Error Types: Automatic mapping for ValidationError, NotFoundError, UnauthorizedError, etc.
Installation
dotnet add package UnambitiousFx.Functional.AspNetCore
Quick Start
Minimal API
using UnambitiousFx.Functional.AspNetCore.Extensions;
using UnambitiousFx.Functional.Results;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddResultHttp();
var app = builder.Build();
// Simple success/failure
app.MapGet("/users/{id:guid}", async (Guid id, IUserService userService) =>
{
var result = await userService.GetUserAsync(id);
return result.ToHttpResult();
});
// With DTO mapping
app.MapGet("/users/{id:guid}", async (Guid id, IUserService userService) =>
{
var result = await userService.GetUserAsync(id);
return result.ToHttpResult(user => new UserDto(user.Id, user.Email));
});
// Created response
app.MapPost("/users", async (CreateUserRequest request, IUserService userService) =>
{
var result = await userService.CreateUserAsync(request);
return result.ToCreatedHttpResult(
user => $"/users/{user.Id}",
user => new UserDto(user.Id, user.Email));
});
app.Run();
MVC Controllers
using Microsoft.AspNetCore.Mvc;
using UnambitiousFx.Functional.AspNetCore.Extensions;
[ApiController]
[Route("api/[controller]")]
public class UsersController : ControllerBase
{
private readonly IUserService _userService;
public UsersController(IUserService userService)
{
_userService = userService;
}
[HttpGet("{id:guid}")]
public async Task<IActionResult> GetUser(Guid id)
{
var result = await _userService.GetUserAsync(id);
return result.ToActionResult(user => new UserDto(user.Id, user.Email));
}
[HttpPost]
public async Task<IActionResult> CreateUser(CreateUserRequest request)
{
var result = await _userService.CreateUserAsync(request);
return result.ToCreatedActionResult(
"GetUser",
user => new UserDto(user.Id, user.Email),
new { id = result.TryGet(out var user, out _) ? user.Id : Guid.Empty });
}
[HttpDelete("{id:guid}")]
public async Task<IActionResult> DeleteUser(Guid id)
{
var result = await _userService.DeleteUserAsync(id);
return result.ToActionResult();
}
}
Error Handling
The library automatically maps standard error types to appropriate HTTP status codes:
| Error Type | HTTP Status Code |
|---|---|
ValidationError |
400 Bad Request |
NotFoundError |
404 Not Found |
UnauthorizedError |
401 Unauthorized |
ConflictError |
409 Conflict |
ExceptionalError |
500 Internal Server Error |
Custom Error |
400 Bad Request |
Example Error Responses
// Validation error → 400 Bad Request
var result = Result.Failure<User>(
new ValidationError(["Email is required", "Password must be at least 8 characters"]));
return result.ToHttpResult();
// Not found → 404 Not Found
var result = Result.Failure<User>(
new NotFoundError("User", userId.ToString()));
return result.ToHttpResult();
// Unauthorized → 401 Unauthorized
var result = Result.Failure<User>(
new UnauthorizedError("Invalid API key"));
return result.ToHttpResult();
Configuration
Basic Configuration
builder.Services.AddResultHttp();
With Problem Details (RFC 7807)
builder.Services.AddResultHttp(options =>
{
options.UseProblemDetails = true;
options.IncludeExceptionDetails = builder.Environment.IsDevelopment();
});
Problem Details response example:
{
"type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
"title": "Validation Failed",
"status": 400,
"detail": "Email is required; Password must be at least 8 characters",
"code": "VALIDATION",
"failures": [
"Email is required",
"Password must be at least 8 characters"
]
}
Custom Error Mappers
Create a custom mapper for domain-specific errors:
public class DomainErrorMapper : IErrorHttpMapper
{
public int? GetStatusCode(IError error)
{
return error switch
{
PaymentRequiredError => 402,
RateLimitError => 429,
_ => null // Let other mappers handle it
};
}
public object? GetResponseBody(IError error)
{
return error switch
{
PaymentRequiredError payment => new
{
error = payment.Code,
message = payment.Message,
subscriptionRequired = true
},
RateLimitError rateLimit => new
{
error = rateLimit.Code,
message = rateLimit.Message,
retryAfter = rateLimit.RetryAfter
},
_ => null
};
}
}
// Register custom mapper
builder.Services.AddResultHttp(options =>
{
options.AddMapper(new DomainErrorMapper());
});
Custom mappers are evaluated before the default mapper, allowing you to override behavior for specific error types.
API Reference
Extension Methods
Minimal API (IResult)
ToHttpResult()- ConvertResultto 200 OK or error statusToHttpResult<TValue>()- ConvertResult<T>to 200 OK with value or error statusToHttpResult<TValue, TDto>()- Convert and map to DTOToCreatedHttpResult<TValue>()- Convert to 201 Created with locationToCreatedHttpResult<TValue, TDto>()- Convert to 201 Created with DTO mapping
MVC Controllers (IActionResult)
ToActionResult()- ConvertResulttoOkResultor error resultToActionResult<TValue>()- ConvertResult<T>toOkObjectResultor error resultToActionResult<TValue, TDto>()- Convert and map to DTOToCreatedActionResult<TValue>()- Convert toCreatedAtActionResultToCreatedActionResult<TValue, TDto>()- Convert with DTO mapping
All extension methods accept an optional IErrorHttpMapper parameter for custom error handling.
Configuration Options
UseProblemDetails- Enable RFC 7807 Problem Details format (default: false)IncludeExceptionDetails- Include stack traces in responses (default: false, use only in development)AddMapper(IErrorHttpMapper)- Add custom error mapper
Best Practices
Use DTO Mapping: Transform domain entities to DTOs at the boundary
return result.ToHttpResult(user => new UserDto(user));Register Result HTTP Services: Always call
AddResultHttp()in your DI configurationbuilder.Services.AddResultHttp(options => { /* configure */ });Enable Problem Details in Production: Use RFC 7807 for consistent error responses
options.UseProblemDetails = true;Create Domain-Specific Error Types: Extend
ErrorBasefor custom errorspublic sealed record PaymentRequiredError(string Message) : ErrorBase("PAYMENT_REQUIRED", Message);Use Specific Error Types: Prefer
ValidationError,NotFoundError, etc. over genericError// Good return Result.Failure<User>(new NotFoundError("User", id)); // Avoid return Result.Failure<User>("User not found");
Thread Safety
All components are thread-safe and designed for concurrent use in ASP.NET Core applications.
AOT Compatibility
This library is fully compatible with Native AOT compilation. All error mappings use static patterns that don't require runtime code generation.
License
This library is part of the UnambitiousFx project and follows the same license.
| 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
- UnambitiousFx.Functional (>= 1.0.6)
-
net8.0
- UnambitiousFx.Functional (>= 1.0.6)
-
net9.0
- UnambitiousFx.Functional (>= 1.0.6)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.