This page captures business rules enforced by SAIF.Web.API.Clients. The library performs no database access, so no rule below carries a Tables or SQL field.
A forwarded user name is normalized to its bare account name — the DOMAIN\ prefix and the @domain suffix are removed.
Trigger
Building the OnBehalfOfUser header, or a direct call to HttpContextExtensions.DomainlessUser.
Enforcement
HttpContextExtensions.DomainlessUser.
Outcome
Downstream APIs receive an unqualified account name. Separators at index ≤ 1 are not stripped, and when both \ and @ are present the @ index is computed on the original string before the \ substring is taken, which can truncate the result incorrectly.
Up to 4 total attempts with a 1000 ms pause each; the final attempt's outcome is what the caller sees. A genuine 401 is retried 3 times before being reported.
A SAIF.API.Exceptions.SAIFException subclass is thrown. With opt-out, the caller must inspect ApiResponse<T>.StatusCode. 3xx redirect statuses pass the guard and are treated as success.
HTTP failure statuses map to specific SAIF exception types: 404 → NoMatchingDataException, 401/403 → UnauthorizedAccessException, 400 → InvalidDataException, all others → InternalServerError.
Trigger
A failed response reaching ExceptionHandler.ToKnownError.
Enforcement
ExceptionHandler.ToKnownError (switch (status)).
Outcome
The thrown exception carries the originating HttpStatusCode. The exception types themselves are defined in SAIF.Web.API.Core (SAIF.API.Exceptions.*, src/SAIF.Web.API.Core/SAIFException.cs).
The exception message must prefer the API's own error text, drawn from the first matching of errormessage, exceptionmessage, message, usermessage; otherwise the raw payload is used.
Trigger
Building a failure exception.
Enforcement
ExceptionHandler.ToKnownError (token loop over the parsed response content).
Outcome
A caller-meaningful message instead of a bare status code. Matching is on the top-level JSON token path only; multiple matches are concatenated as separate lines.
Date values on the wire are handled in the client's configured time-zone mode (default Local), consistently for both serialization and deserialization.
Single-value results are coerced by target type: string is unquoted, primitives are converted directly, byte[] is base64-decoded, and the literal null yields the type default.
Trigger
GetResult<T> on a completed GET.
Enforcement
JsonHttpClient.GetResult<T>.
Outcome
The caller receives a materialized T without writing conversion code. A malformed or quoted primitive payload throws FormatException/InvalidCastException uncaught to the caller.
The responding server instance is captured from the Server-Name response header and surfaced on the response envelope.
Trigger
Envelope projection.
Enforcement
JsonHttpClient.GetApiResponse<T>.
Outcome
ApiResponse<T>.Server (from SAIF.Web.API.Core) carries the server name, or null when the header is absent. Header-name match is case-sensitive ordinal.
A payload that fails to deserialize does not propagate the parse error: for a string target the exception message is returned as the value; for any other target an empty result is returned.
No exception reaches the caller from the parse stage. This masks parse failures and can surface an exception message as business data — flagged as a defect risk.
The failure message is chosen from the richest available source: response content first, then transport error information, then the status code and description.
Trigger
Building a failure exception.
Enforcement
ExceptionHandler.GetContent.
Outcome
An error message is always produced (possibly empty). The transport-error branch has inverted conditional logic and yields a blank message when there is no response content*.
No input validation exists for methodUriSegment, serviceBaseUri, requestBody, or ClientInfo; there is no authorization decision logic, no rate limiting, circuit breaking, or caching, and no logging or auditing of any kind (verified absences).
EnvironmentSetting is a public key/value DTO with no consumer anywhere in this repository and enforces no business rule*.
Note: Statements marked with * were inferred from code structure, naming conventions, or
contextual evidence where direct confirmation was not available in source artifacts. Statements
marked with ** could not be confirmed; treat as unknown until verified.