Skip to content

SAIF.NetCore.HttpClients architecture

SAIF.NetCore.HttpClients is a thin, stateful facade over RestSharp 106 that implements the IHttpJsonClient/IHttpRequestResult contracts declared in SAIF.NetCore.ServiceContracts, giving every other SAIF.NetCore implementation library and consuming application a single reusable way to issue outbound JSON HTTP requests with bearer, basic, or NTLM authentication, retry-on-status-code behavior, and caller-identity header propagation.

Responsibilities

  • Implement IHttpJsonClient and IHttpRequestResult (HttpJsonClient, HttpRequestResult) so that callers program only against the ServiceContracts interfaces.
  • Hold per-instance request state: base URL, ClientInfo (machine name, application ID, user name), timeout override, HttpClientOptions, bearer token, custom headers, NTLM authenticator, and the last RequestResult.
  • Build and execute each RestSharp request, including identity headers (Client-Name, Application-ID, Client-User), optional JSON body, and configured authentication.
  • Apply retry-on-status-code behavior for transient failures, sleeping between attempts on the calling thread.
  • Normalize the RestSharp response into an IHttpRequestResult, including status, content, headers, response date, and error-message extraction.
  • Offer result-conversion helpers (HttpJsonClientExtensions, HttpRequestResult.GetResult<T>/GetContent<T>) so callers can deserialize responses without depending on RestSharp or Newtonsoft types directly.

Component boundaries

  • HttpJsonClient (implements IHttpJsonClient): the request facade — construction, authentication setup, verb methods (Get/Post/Put/Patch/Delete and their async equivalents), and the private request/execute/retry pipeline.
  • HttpRequestResult (implements IHttpRequestResult): the result model populated by HttpJsonClient after each call.
  • HttpJsonClientExtensions (static): content/header conversion helpers (ToReturnType<T>, GetHeaderValue<T>) and two unused helpers (CleanUrlPart, ToHttpContent).
  • ClientInfo: the caller-identity value object (machine name, application ID, user name) propagated as request headers.
  • ClientNotInitializedException: thrown when a verb method is called before both ClientInfo and the base URL are set.
  • Contract boundary: IHttpJsonClient, IHttpRequestResult, HttpClientOptions, and HttpClientSerializer live in SAIF.NetCore.ServiceContracts — this library supplies the implementation only.

Interaction model

  • Every target URL is caller-supplied at request time; this library hard-codes no SAIF web API endpoint of its own, so it behaves purely as a generic HTTP transport used by other libraries and applications.
  • Consumers resolve IHttpJsonClient through dependency injection — SAIF.NetCore.ServiceExtensions registers it as Transient (services.AddTransient<IHttpJsonClient, HttpJsonClient>()) — then call SetClientInfo/SetOptions/authentication setters before issuing a request, because the DI container can only satisfy the parameterless constructor*.
  • The mutable RequestResult/Headers instance state makes a single HttpJsonClient instance unsafe to share across concurrent requests, consistent with the IHttpJsonClient XML doc's "intended to be used as a transient" guidance*.
  • A new RestClient is created for every call and every retry attempt; retries block a thread-pool thread with Thread.Sleep rather than an asynchronous delay.
  • No SAIF library-to-library delegation happens at runtime beyond the shared contract types — the request execution path is self-contained.

Architecture diagram

The following diagram shows the request pipeline from a verb-method call through RestSharp execution and retry to the returned result.

flowchart TD
    A["Verb method<br>e.g. Get/PostAsync"] --> B[ValidateInitialization]
    B -->|"clientInfo or baseUri null"| X[ClientNotInitializedException]
    B --> C[ExecuteRestCall]
    C --> D["CreateClient:<br>new RestClient, authenticator,<br>serializer, timeout"]
    C --> E["CreateRequest:<br>body, identity headers,<br>bearer, custom headers"]
    D --> F["client.Execute(request)"]
    E --> F
    F --> G{"Status in RetryOnStatusCodes<br>and retries remain?"}
    G -->|yes| H["Thread.Sleep(RetryDelayInMs)"] --> C
    G -->|no| I[CreateRequestResult]
    I --> J["RequestResult field updated<br>and returned to caller"]

Sequence diagram

The following diagram shows a typical authenticated JSON request issued through the client.

sequenceDiagram
    participant Consumer
    participant HttpJsonClient
    participant RestClient as "RestSharp RestClient"
    participant Target as "Caller-supplied endpoint"
    Consumer->>HttpJsonClient: SetClientInfo / SetOptions / BearerToken = token
    Consumer->>HttpJsonClient: GetAsync(resource)
    HttpJsonClient->>HttpJsonClient: ValidateInitialization
    HttpJsonClient->>RestClient: CreateClient + CreateRequest
    RestClient->>Target: HTTP request with identity + auth headers
    Target-->>RestClient: HTTP response
    RestClient-->>HttpJsonClient: IRestResponse
    HttpJsonClient-->>Consumer: IHttpRequestResult

Design constraints

  • No SAIF web API endpoint is hard-coded; every URL is supplied by the caller at request time, so this library cannot be documented against a fixed API surface.
  • The generic *Async<T> overloads are [Obsolete("This method not thread safe and has been depracated. Please use the overload <Verb>Async(string urlPart, object body).", true)] and cannot be called — only the non-generic async/sync verb methods and the HttpRequestResult conversion helpers are usable.
  • RestSharp 106.11.7 is several majors behind the current RestSharp release line, and this library has no DI/hosting surface of its own (no IServiceCollection extensions, middleware, or hosted services).
  • The library performs no configuration binding (IConfiguration, IOptions<T>, IEnvironmentClient); all behavior-defining values — retry count, timeout ceiling, header names — are hard-coded constants or set programmatically through HttpClientOptions/SetClientInfo.

Note: Statements marked with * were inferred from code structure, naming conventions, or contextual evidence where direct confirmation was not available in source artifacts.

Sources

  • src/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/HttpJsonClient/HttpJsonClient.cs
  • src/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/HttpJsonClient/HttpJsonClientExtensions.cs
  • src/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/HttpJsonClient/HttpRequestResult.cs
  • src/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/ClientInfo.cs
  • src/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/Exceptions/ClientNotInitializedException.cs
  • Architecture.md