REslava.Result.Http
1.36.0
Prefix Reserved
dotnet add package REslava.Result.Http --version 1.36.0
NuGet\Install-Package REslava.Result.Http -Version 1.36.0
<PackageReference Include="REslava.Result.Http" Version="1.36.0" />
<PackageVersion Include="REslava.Result.Http" Version="1.36.0" />
<PackageReference Include="REslava.Result.Http" />
paket add REslava.Result.Http --version 1.36.0
#r "nuget: REslava.Result.Http, 1.36.0"
#:package REslava.Result.Http@1.36.0
#addin nuget:?package=REslava.Result.Http&version=1.36.0
#tool nuget:?package=REslava.Result.Http&version=1.36.0
REslava.Result.Http
HttpClient extensions that return Result<T> instead of throwing.
Map HTTP 4xx/5xx status codes to typed domain errors and wrap network failures in ExceptionError — no try/catch, no manual status code checks.
What It Does
// Before — boilerplate repeated in every service/repository
var response = await httpClient.GetAsync($"/api/users/{id}");
if (!response.IsSuccessStatusCode)
return Result<User>.Fail(new NotFoundError("User", id));
var user = await response.Content.ReadFromJsonAsync<User>();
return Result<User>.Ok(user!);
// After
Result<User> result = await httpClient.GetResult<User>($"/api/users/{id}");
Result<Order> posted = await httpClient.PostResult<CreateOrderDto, Order>("/api/orders", dto);
Result<Order> updated = await httpClient.PutResult<UpdateOrderDto, Order>($"/api/orders/{id}", dto);
Result deleted = await httpClient.DeleteResult($"/api/orders/{id}");
Quick Start
dotnet add package REslava.Result
dotnet add package REslava.Result.Http
using REslava.Result.Http;
// In a service/repository
public async Task<Result<User>> GetUserAsync(int id, CancellationToken ct = default)
=> await _httpClient.GetResult<User>($"/api/users/{id}", cancellationToken: ct);
public async Task<Result<User>> CreateUserAsync(CreateUserDto dto, CancellationToken ct = default)
=> await _httpClient.PostResult<CreateUserDto, User>("/api/users", dto, cancellationToken: ct);
Available Methods
| Method | Returns |
|---|---|
GetResult<T>(string \| Uri) |
Task<Result<T>> |
PostResult<TBody, TResponse>(string, TBody) |
Task<Result<TResponse>> |
PutResult<TBody, TResponse>(string, TBody) |
Task<Result<TResponse>> |
DeleteResult(string) |
Task<Result> |
DeleteResult<T>(string) |
Task<Result<T>> |
All methods accept optional HttpResultOptions? and CancellationToken.
Default Error Mapping
| HTTP Status | Error Type | Default Message |
|---|---|---|
| 2xx | ✅ Success — deserializes body | — |
| 404 | NotFoundError |
"Resource not found" |
| 401 | UnauthorizedError |
"Authentication required" |
| 403 | ForbiddenError |
"Access denied" |
| 409 | ConflictError |
"A conflict occurred" |
| 422 | ValidationError |
"Validation failed" |
| Other 4xx/5xx | Error |
"HTTP {code}: {reason}" |
| Network exception | ExceptionError |
Exception message |
Configuration
Customise JSON deserialization and/or the status code → error mapping:
var options = new HttpResultOptions
{
// Custom JSON options (default: JsonSerializerDefaults.Web)
JsonOptions = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower
},
// Custom status code mapper — completely replaces the built-in defaults
StatusCodeMapper = (statusCode, reasonPhrase) => statusCode switch
{
HttpStatusCode.NotFound => new NotFoundError("Order", requestedId),
HttpStatusCode.Conflict => new ConflictError("Order", "number", orderNumber),
_ => new Error($"HTTP {(int)statusCode}: {reasonPhrase}")
}
};
Result<Order> result = await httpClient.GetResult<Order>($"/api/orders/{id}", options);
Symmetry with Server Side
REslava.Result.Http completes the full round-trip with the server-side source generator:
SERVER (outbound): Result<T> → IResult → HTTP response ← REslava.Result.SourceGenerators
CLIENT (inbound): HTTP response → Result<T> ← REslava.Result.Http
A server that returns Result<T> via [SmartEndpoint] can be consumed by a client that
gets Result<T> back — no manual status-code inspection at either end.
Requires
- REslava.Result (automatically installed as a dependency)
Links
MIT License | .NET 8 / 9 / 10
| 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
- REslava.Result (>= 1.36.0)
-
net8.0
- REslava.Result (>= 1.36.0)
-
net9.0
- REslava.Result (>= 1.36.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.