---
title: SAIF.NetCore.HttpClients architecture
summary: Architectural responsibilities, boundaries, and interaction patterns.
status: draft
owner: Framework
last_reviewed: 2026-10-02
library: SAIF.NetCore.HttpClients
reference_type: package
---

# 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.

```mermaid
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.

```mermaid
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`.

## Related documentation
- [Index](index.md)
- [Overview](overview.md)
- [Dependencies](dependencies.md)
- [Operations and processes](operations-processes.md)
- [Business rules](business-rules.md)
- [Security and authorization](security-authorization.md)
- [API reference](api/index.md)
- [SAIF.NetCore.ServiceContracts overview](../saif-netcore-servicecontracts/overview.md)

> **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`
