HTTP Client
ArturRios.Util.WebApi gives you a thin base for typed clients that call another web API: BaseWebApiClient owns a shared HttpGateway, and…ArturRios.Util.WebApi gives you a thin base for typed clients that call another web API:
BaseWebApiClient owns a shared HttpGateway, and BaseWebApiClientRoute groups related endpoints
under a base URL with helpers for authenticating and carrying the resulting bearer token.
BaseWebApiClient
BaseWebApiClient is abstract and exposes a protected HttpGateway Gateway that every route object uses
to issue requests. It has two constructors:
BaseWebApiClient(HttpClient httpClient)— wraps an existingHttpClient. Prefer this one: it lets the client be created throughIHttpClientFactory(viaAddHttpClient<T>()), which manages the underlyingHttpClient’s lifetime and connection pooling for you and is what lets you attach message handlers likeTracePropagationHandler.BaseWebApiClient(string baseUrl)— constructs its ownHttpClientwithBaseAddress = baseUrl. Simpler for quick/manual use, but you loseIHttpClientFactory’s pooling and the ability to attach handlers through DI.
Both constructors call the abstract SetRoutes() method before returning, so a derived class implements
it to instantiate its route objects against the shared Gateway:
public class MyApiClient : BaseWebApiClient
{
public AccountsRoute Accounts { get; private set; } = null!;
public MyApiClient(HttpClient httpClient) : base(httpClient) { }
protected override void SetRoutes()
{
Accounts = new AccountsRoute(Gateway);
}
}
BaseWebApiClientRoute
BaseWebApiClientRoute(HttpGateway gateway) is the base for a group of related routes on the remote API.
It exposes:
abstract string BaseUrl— the base path this route group is mounted under (e.g./accounts).AuthenticateAsync(Credentials credentials, string authRoute)— postscredentialstoauthRoutevia the sharedGatewayand returns the resultingAuthentication. ThrowsWebApiClientExceptionif the response comes back with no body.Authorize(string authToken)— setsGateway.Client.DefaultRequestHeaders.Authorizationto aBearerheader carryingauthToken. This is a plain assignment, so it’s idempotent — calling it again (e.g. after re-authenticating) simply replaces the previous header rather than accumulating anything.AuthenticateAndAuthorizeAsync(Credentials credentials, string authRoute)— callsAuthenticateAsyncthenAuthorizewith the returned token, in one call. ThrowsWebApiClientExceptionif the returnedAuthenticationisn’tValidor carries no token, so a failed login never leaves the client with an empty bearer header.
public class AccountsRoute(HttpGateway gateway) : BaseWebApiClientRoute(gateway)
{
public override string BaseUrl => "/accounts";
public Task LoginAsync(Credentials credentials) =>
AuthenticateAndAuthorizeAsync(credentials, $"{BaseUrl}/login");
}
After LoginAsync completes, every subsequent call made through the shared Gateway — from Accounts
or any other route object on the same client — carries the Authorization: Bearer header, since they all
share the same underlying HttpClient.
WebApiClientException
A plain Exception subclass raised when a BaseWebApiClientRoute operation can’t complete — when
AuthenticateAsync gets a response with no body, or when AuthenticateAndAuthorizeAsync gets an
Authentication that isn’t Valid or has no token. Catch it around login calls; when calling
AuthenticateAsync directly, a normal authentication failure is still surfaced through the
Authentication.Valid flag instead.
Pairing with TracePropagationHandler
Because the HttpClient-based constructor is built for IHttpClientFactory, you can attach
TracePropagationHandler (see Middleware & Diagnostics) with the
AddTracePropagation() extension (HttpClientBuilderExtensions, in ArturRios.Util.WebApi.Extensions), so every outgoing call from the
client carries the current request’s W3C trace id (traceparent, plus tracestate when present):
builder.Services.AddHttpClient<MyApiClient>()
.AddTracePropagation();
AddTracePropagation() registers the handler with the container if it isn’t already, so no separate
AddTransient<TracePropagationHandler>() call is needed.
flowchart LR
Ctor["MyApiClient(HttpClient)"] --> SetRoutes["SetRoutes()"]
SetRoutes --> Accounts["AccountsRoute(Gateway)"]
Accounts -- "AuthenticateAndAuthorizeAsync" --> Login["POST /accounts/login"]
Login -- "Authentication.Token" --> Authorize["Authorize(token)"]
Authorize --> Header["Gateway.Client default Authorization header"]
Header --> Calls["Subsequent calls via Gateway"]
Calls -- "TracePropagationHandler" --> Downstream["Downstream API"]Where to next
- Middleware & Diagnostics —
TracePropagationHandlerand the trace id it propagates. - Security —
Credentials,Authenticationand the login flow this client typically calls into. - Responses — the
ArturRios.Outputenvelopes returned by the APIs these clients call.