Skip to content

Http Client β€” REslava.Result.Http

REslava.Result.Http wraps HttpClient calls so every HTTP response and network failure becomes a typed Result<T>. The client-side complement to ToIResult() β€” completes the full round-trip.

Install: dotnet add package REslava.Result.Http

10.6.1. GetResult<T> β€” Type-safe GET

Sends a GET request and deserializes the body on 2xx; maps 4xx/5xx to typed domain errors automatically. Accepts both string and Uri overloads.

using REslava.Result.Http;

// string overload
Result<User> user = await httpClient.GetResult<User>("/api/users/42");

user.Match(
    onSuccess: u  => Console.WriteLine($"Got {u.Name}"),
    onFailure: errors => Console.WriteLine(errors[0].Message));
    // 404 β†’ NotFoundError, 401 β†’ UnauthorizedError, network failure β†’ ExceptionError

// Uri overload
Result<User> user2 = await httpClient.GetResult<User>(new Uri("https://api.example.com/users/42"));

10.6.2. PostResult<TBody, TResponse> β€” Type-safe POST

Sends a POST request with a JSON-serialized body and returns Result<TResponse>.

Result<User> created = await httpClient.PostResult<CreateUserRequest, User>(
    "/api/users",
    new CreateUserRequest("Alice", "alice@example.com"));

if (created.IsSuccess)
    Console.WriteLine($"Created user {created.Value.Id}");

10.6.3. PutResult<TBody, TResponse> β€” Type-safe PUT

Sends a PUT request with a JSON-serialized body and returns Result<TResponse>.

Result<User> updated = await httpClient.PutResult<UpdateUserRequest, User>(
    "/api/users/42",
    new UpdateUserRequest("Alice Updated"));

updated.Match(
    onSuccess: u    => Console.WriteLine($"Updated: {u.Name}"),
    onFailure: errors => Console.WriteLine(errors[0].Message));

10.6.4. DeleteResult β€” Type-safe DELETE (no body)

Sends a DELETE request and returns a non-generic Result β€” use when the API returns no body on success (204 No Content pattern).

Result deleted = await httpClient.DeleteResult("/api/users/42");

if (deleted.IsSuccess)
    Console.WriteLine("Resource deleted");

10.6.5. DeleteResult<T> β€” Type-safe DELETE (with response body)

Sends a DELETE request and deserializes the response body into Result<T> β€” use when the API returns the deleted resource or a confirmation object.

Result<DeletedUserDto> result = await httpClient.DeleteResult<DeletedUserDto>("/api/users/42");

if (result.IsSuccess)
    Console.WriteLine($"Archived at: {result.Value.ArchivedAt}");

10.6.6. HttpResultOptions β€” Custom JSON & Error Mapping

Configures JSON deserialization options and/or replaces the entire status-code-to-error mapping with a custom delegate. Pass as the last parameter to any extension method.

var options = new HttpResultOptions
{
    // Custom JSON serialization (default: JsonSerializerDefaults.Web)
    JsonOptions = new JsonSerializerOptions
    {
        PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower
    },

    // Complete override of the status-code β†’ IError mapping
    StatusCodeMapper = (statusCode, reasonPhrase) => statusCode switch
    {
        (HttpStatusCode)429 => new RateLimitError("Too many requests β€” back off and retry"),
        _                   => new Error($"HTTP {(int)statusCode}: {reasonPhrase}")
    }
};

Result<User> user = await httpClient.GetResult<User>("/api/users/42", options);

10.6.7. Status Code β†’ Error Mapping (defaults)

HTTP Status Domain Error Default Message
404 Not Found NotFoundError "Resource not found"
401 Unauthorized UnauthorizedError "Authentication required"
403 Forbidden ForbiddenError "Access denied"
409 Conflict ConflictError "A conflict occurred"
422 Unprocessable Entity ValidationError "Validation failed"
Other 4xx / 5xx Error "HTTP {code}: {reasonPhrase}"
Network / timeout ExceptionError ex.Message

Override any or all mappings via HttpResultOptions.StatusCodeMapper.

See REslava.Result.Http README for the full API reference.