This page captures business rules enforced by SAIF.NetCore.HttpClients in building, authenticating, executing, and interpreting outbound JSON HTTP requests.
BR-HTTP-01 Client initialization required before any request¶
Field
Value
Statement
Every verb method requires both ClientInfo and a base URI to have been set; otherwise a ClientNotInitializedException ("Client info and base URL not initialized.") is thrown before any network call is attempted.
Trigger
Any call to Delete/Get/Patch/Post/Put or their async equivalents.
Enforcement
HttpJsonClient.ValidateInitialization
Outcome
The request is rejected locally with ClientNotInitializedException; no HTTP call is made.
BR-HTTP-02 Base URL required in the two-argument constructor¶
Field
Value
Statement
baseUrl must not be null in the (baseUrl, ClientInfo) constructor, which throws ArgumentNullException when it is; the clientInfo parameter on this overload is not null-checked.
Trigger
Construction via new HttpJsonClient(baseUrl, clientInfo).
Enforcement
HttpJsonClient constructor
Outcome
A null base URL fails fast with ArgumentNullException; a null clientInfo is accepted and surfaces later as ClientNotInitializedException (see BR-HTTP-01).
BR-HTTP-04 Retry on configured response status codes¶
Field
Value
Statement
Responses whose status is in RetryOnStatusCodes (default 401 Unauthorized, 502 BadGateway, 503 ServiceUnavailable) are retried up to 3 additional times, for up to 4 attempts in total.
Trigger
A response status matching the configured retry set, with retries remaining.
Enforcement
HttpJsonClient.ExecuteRestCall
Outcome
The request is resubmitted (new RestClient and request) until the status is no longer retryable or attempts are exhausted.
A timeout override takes effect only when the value passed to SetClientInfo is greater than 0 and at most 600000 ms (10 minutes). A value greater than 0 replaces the stored override; a value of 0 or less leaves any previously stored override unchanged (it does not reset the client to the RestSharp default). CreateClient applies the stored override only when it is greater than 0 and at most 600000 ms, and falls back to the RestSharp default otherwise.
Trigger
SetClientInfo(baseUrl, userName, machineName, applicationId, msTimeoutOverride) and client creation for each request.
A non-positive override on a later SetClientInfo call does not clear a previously stored positive override; the RestSharp default is used only when no valid override has ever been stored, or when the stored override is outside the 1–600000 ms range at request time.
BR-HTTP-07 Custom headers cleared after each request unless persisted¶
Field
Value
Statement
Custom entries in the Headers dictionary are cleared after each request is built unless HttpClientOptions.PersistHeadersAfterRequest is true (default false).
Trigger
Every call to CreateRequest, including each retry attempt.
Enforcement
HttpJsonClient.CreateRequest
Outcome
With default options, custom headers (including a Basic Authorization header) are present on attempt 1 only and absent from retries; with persistence enabled, they remain on the instance for every subsequent request.
When the BearerToken property is non-empty, an Authorization header built from the token is added to every request.
Trigger
CreateRequest for any request issued while BearerToken is set.
Enforcement
HttpJsonClient.CreateRequest
Outcome
The bearer token is attached to the outgoing request; because it is a property rather than a Headers entry, it is never cleared by BR-HTTP-07 and is resent on every retry.
BR-HTTP-10 NTLM authentication, explicit or current identity¶
Field
Value
Statement
SetNtlmAuthentication(userName, password) configures NTLM with explicit credentials, and the parameterless SetNtlmAuthentication() configures NTLM using the current process identity; the resulting authenticator is assigned to every RestClient created for a request.
Trigger
A call to either SetNtlmAuthentication overload, followed by any request.
When HttpClientOptions.Serializer is NewtonsoftJson, the RestSharp client uses the Newtonsoft serializer; otherwise (Default) it uses RestSharp's built-in JsonDeserializer for application/json. This selection governs RestSharp's own internal handling only; it does not affect how this library converts results.
Trigger
CreateClient for every request.
Enforcement
HttpJsonClient.CreateClient
Outcome
CreateClient configures the RestSharp client's handler/serializer for the chosen Serializer value, but ExecuteRestCall calls the non-generic client.Execute(request) and CreateRequestResult copies response.Content as a raw string — RestSharp's configured serializer never parses the response. Typed conversion via GetResult<T>/GetContent<T> always follows ToReturnType<T> (OP-05), which uses Convert.ChangeType for value types/string and JsonConvert.DeserializeObject<T> (Newtonsoft) for all other types, independently of HttpClientOptions.Serializer.
BR-HTTP-12 Caller-identity headers on every request¶
Field
Value
Statement
Every request carries three caller-identity headers: Client-Name (machine name), Application-ID (application ID), and Client-User (user name), sourced from the configured ClientInfo.
Trigger
CreateRequest for every request.
Enforcement
HttpJsonClient.CreateRequest
Outcome
The receiving service observes caller-asserted identity headers on every call; these are unauthenticated assertions, not signed claims.
A null RestSharp response is handled defensively in CreateRequestResult, producing an IHttpRequestResult with IsSuccessful = false and content "Rest response was null". In the current call path, ExecuteRestCall reads response.StatusCode immediately after client.Execute(request) and before any null check, so a null response throws NullReferenceException there instead of reaching CreateRequestResult; this branch is therefore effectively unreachable through the public verb methods as currently implemented*.
Trigger
RestSharp returns a null IRestResponse for a request.
Enforcement
HttpJsonClient.CreateRequestResult
Outcome
The defensive null-handling code exists in CreateRequestResult, but callers going through the public verb methods receive a NullReferenceException rather than this well-formed failure result if RestSharp ever returns null*.
ResponseDate is parsed from the response's date header using a case-insensitive SingleOrDefault match followed by DateTime.TryParse. The result is null when the header is missing, is not a string value, or cannot be parsed. If more than one header named date (case-insensitive) is present, SingleOrDefault throws InvalidOperationException before parsing is attempted.
Trigger
Result construction after every request.
Enforcement
HttpJsonClient.GetDateHeader
Outcome
IHttpRequestResult.ResponseDate is populated only when exactly one date header is present and it is a parseable string; a duplicate date header aborts result construction with InvalidOperationException instead of yielding a null ResponseDate.
ErrorMessage is derived in order: (1) RestSharp's own ErrorMessage; (2) top-level JSON tokens errormessage/exceptionmessage/message joined by newline; (3) on JSON-parse failure, the raw response content; (4) if still empty, "{code} - {description}". The empty-token-match fallback to raw content is inferred from an empty-sequence Aggregate exception being caught.*
Trigger
Result construction for an unsuccessful response.
Enforcement
HttpJsonClient.PopulateRequestResultErrorContent
Outcome
Callers always receive a non-empty ErrorMessage for failed requests, in the priority order above.
BR-HTTP-16 Duplicate Authorization key guard on Basic authentication¶
Field
Value
Statement
SetBasicAuthentication throws ArgumentException if an Authorization key already exists in Headers — for example when headers are persisted from a prior call.*
Trigger
A second call to SetBasicAuthentication on an instance with PersistHeadersAfterRequest = true and an existing Authorization header.
BR-HTTP-19 Content conversion trims quotes and honors culture¶
Field
Value
Statement
ToReturnType<T> strips exactly one leading and trailing double-quote pair, preserving any inner escaped quotes, only when T is string and the content both starts and ends with "; the unwrapped (or, for all other cases, unmodified) content is then converted with Convert.ChangeType using the supplied culture when T is a value type or string, and through JSON deserialization using the culture in the serializer settings for all other types. A quoted numeric string (for example "5" converting to int) is not unquoted by this path, because the quote-stripping condition only matches when T is string.
Trigger
A caller invokes GetContent<T>()/GetContent<T>(CultureInfo) or the extension method ToReturnType<T> directly.
Enforcement
HttpJsonClientExtensions.ToReturnType<T>
Outcome
Content is converted according to its target type and the requested culture, regardless of the response's success status.
GetHeaderValue<T> performs a case-insensitive lookup by header name, returns the default value when the match is null, and unwraps Nullable<T> to its underlying type for conversion.
Trigger
A caller invokes IHttpRequestResult.GetHeaderValue<T>(name) or the underlying extension method.
Enforcement
HttpJsonClientExtensions.GetHeaderValue<T>
Outcome
Header lookups succeed regardless of header-name casing; a case-variant duplicate causes the lookup to throw.
BR-HTTP-21 Generic async verb methods are disabled¶
Field
Value
Statement
The generic *Async<T> verb method overloads are marked [Obsolete("This method not thread safe and has been depracated. Please use the overload <Verb>Async(string urlPart, object body).", true)], making them a compile-time error to call.
Trigger
Any attempt to call DeleteAsync<T>, GetAsync<T>, PatchAsync<T>, PostAsync<T>, or PutAsync<T>.
Enforcement
HttpJsonClient class declarations ([Obsolete] attribute)
Outcome
Callers must use the non-generic async verb methods together with HttpRequestResult.GetResult<T>/GetContent<T> instead.
Note: Statements marked with * were inferred from code structure, naming
conventions, or contextual evidence where direct confirmation was not available
in source artifacts.