Skip to content

SAIF.NetCore.HttpClients business rules

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.
Source src/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/HttpJsonClient/HttpJsonClient.cs

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).
Source src/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/HttpJsonClient/HttpJsonClient.cs

BR-HTTP-03 ClientInfo requires non-null identity values

Field Value
Statement ClientInfo requires a non-null machine name, application ID, and user name; each throws ArgumentNullException individually when null.
Trigger Construction of a ClientInfo instance.
Enforcement ClientInfo constructor
Outcome Construction fails immediately with ArgumentNullException naming the missing value.
Source src/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/ClientInfo.cs

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.
Source src/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/HttpJsonClient/HttpJsonClient.cs

BR-HTTP-05 Delay between retry attempts

Field Value
Statement Each retry waits RetryDelayInMs (default 1000 ms) using a blocking Thread.Sleep before resubmitting.
Trigger A retryable response status with retries remaining (see BR-HTTP-04).
Enforcement HttpJsonClient.ExecuteRestCall
Outcome The calling thread (or a thread-pool thread for async calls) blocks for the configured delay between attempts.
Source src/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/HttpJsonClient/HttpJsonClient.cs

BR-HTTP-06 Timeout override bounds

Field Value
Statement 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.
Enforcement HttpJsonClient.SetClientInfo; HttpJsonClient.CreateClient
Outcome 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.
Source src/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/HttpJsonClient/HttpJsonClient.cs

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.
Source src/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/HttpJsonClient/HttpJsonClient.cs

BR-HTTP-08 Bearer token header

Field Value
Statement 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.
Source src/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/HttpJsonClient/HttpJsonClient.cs

BR-HTTP-09 Basic authentication header encoding

Field Value
Statement SetBasicAuthentication(userName, password) adds an Authorization header whose value is Basic followed by the Base64 encoding of user:password.
Trigger A call to SetBasicAuthentication.
Enforcement HttpJsonClient.SetBasicAuthentication; HttpJsonClient.EncodeForBasicAuthentication
Outcome The Headers dictionary gains a Basic Authorization entry, subject to the clearing behavior in BR-HTTP-07.
Source src/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/HttpJsonClient/HttpJsonClient.cs

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.
Enforcement HttpJsonClient.SetNtlmAuthentication; HttpJsonClient.CreateClient
Outcome Every request (including retries) performs NTLM authentication using the configured credential source.
Source src/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/HttpJsonClient/HttpJsonClient.cs

BR-HTTP-11 Serializer selection

Field Value
Statement 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.
Source src/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/HttpJsonClient/HttpJsonClient.cs

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.
Source src/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/HttpJsonClient/HttpJsonClient.cs

BR-HTTP-13 Null response handling

Field Value
Statement 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*.
Source src/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/HttpJsonClient/HttpJsonClient.cs

BR-HTTP-14 Response date parsing

Field Value
Statement 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.
Source src/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/HttpJsonClient/HttpJsonClient.cs

BR-HTTP-15 Error message derivation order

Field Value
Statement 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.
Source src/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/HttpJsonClient/HttpJsonClient.cs

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.
Enforcement HttpJsonClient.SetBasicAuthentication (Headers.Add)
Outcome Rotating Basic credentials on a persisted instance fails until the existing Authorization entry is removed manually.
Source src/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/HttpJsonClient/HttpJsonClient.cs

BR-HTTP-17 Error exception populated only for 4xx

Field Value
Statement ErrorException is populated only when the response status is in the 4xx range.
Trigger Result construction for a 4xx response.
Enforcement HttpJsonClient.PopulateRequestResultErrorContent
Outcome Non-4xx failures (for example 5xx) do not populate ErrorException, only ErrorMessage/Content.
Source src/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/HttpJsonClient/HttpJsonClient.cs

BR-HTTP-18 GetResult returns default on failure

Field Value
Statement GetResult<T>() returns default(T) when IsSuccessful is false, instead of attempting to convert the response content.
Trigger A caller invokes GetResult<T>() or GetResult<T>(CultureInfo) on an unsuccessful result.
Enforcement HttpRequestResult.GetResult<T>
Outcome Callers never receive a converted value derived from a failed response's content through GetResult<T>.
Source src/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/HttpJsonClient/HttpRequestResult.cs

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.
Source src/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/HttpJsonClient/HttpJsonClientExtensions.cs

BR-HTTP-20 Case-insensitive header lookup

Field Value
Statement 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.
Source src/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/HttpJsonClient/HttpJsonClientExtensions.cs

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.
Source src/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/HttpJsonClient/HttpJsonClient.cs

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/HttpRequestResult.cs
  • src/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/HttpJsonClient/HttpJsonClientExtensions.cs
  • src/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/ClientInfo.cs