Skip to content

GetJsonResponse

GetJsonResponse is the single private method through which every public verb method on JsonHttpClient executes its HTTP call. Because header injection, authentication, timeout, retry, and success-enforcement all happen here, no public method can bypass them individually — this is the core operation of the library.

Field Value
Operation ID OP-05
Trigger Every public verb method (GetResult<T>, GetResponse<T>, PostJson<T>, PutJson<T>, PatchJson<T>, DeleteJson<T>); also self-recursive on retry.
Inputs URI segment, HTTP method, optional body, remaining retry budget (default 3).
Outputs Internal JsonResponse.
Primary components JsonHttpClient.GetJsonResponse(string, Method, object, int)
Entry point src/SAIF.Web.API.Clients/JsonHttpClient.cs

Steps

  1. Construct new RestClient(_serviceBaseUri). A new client is created per call — there is no connection/handler reuse across calls*.
  2. Assign client.Authenticator = _authenticator (NTLM by default) (BR-CLI-001).
  3. client.ClearHandlers() then client.AddHandler("application/json", () => new JsonDeserializer()) — replaces all default content handlers so only application/json is deserializable (BR-CLI-015).
  4. If the response's Content-Type is not JSON, no deserialized object is produced by RestSharp; the raw Content string is still available downstream.
  5. If TimeoutOverride > 0, set client.Timeout (BR-CLI-010).
  6. If TimeoutOverride is 0, the platform HttpWebRequest default remains in force.
  7. Build new RestRequest(methodUriSegment, method) and assign request.JsonSerializer = new JsonSerializer(DateTimeZoneHandling) (BR-CLI-011).
  8. Embedded logic: construct a Newtonsoft.Json.JsonSerializer with the configured date-time-zone handling.

Origin: This step is implemented in Serialize (JsonSerializer) (SAIF.Web.API.Clients) and executed on behalf of this operation. 6. If body != null, request.AddJsonBody(body) (BR-CLI-011). - If body is null, no body is added — valid for GET or a body-less DELETE. 7. Add headers Client-Name = _clientInfo.MachineName, Application-ID = _clientInfo.ApplicationId, Client-User = _clientInfo.UserId (BR-CLI-002). - On failure: _clientInfo is not null-checked — a null ClientInfo throws NullReferenceException here. Individual null property values are added as-is. 8. If SetOnBehalfOfUserHeaders, add header OnBehalfOfUser = HttpContext.Current.DomainlessUser() (BR-CLI-003, BR-CLI-004). - Embedded logic: strip the DOMAIN\ prefix or @domain suffix from the ambient user's identity name.

Origin: This step is implemented in DomainlessUser (SAIF.Web.API.Clients) and executed on behalf of this operation. - On failure: requires an ambient ASP.NET HttpContext; a null context throws NullReferenceException. 9. client.Execute(request) — synchronous, blocking. - On failure: transport failure does not throw here; RestSharp returns status 0 with ErrorException populated. 10. If retries > 0 and RetryOnStatusCodes contains the response status: Thread.Sleep(RetryDelayInMs), then recurse with retries - 1 (BR-CLI-005). - Default budget is 3 retries (4 total attempts) at 1000 ms each. Status 0 (transport failure), 401, 502, 503 are retried by default. The retried call rebuilds the client and re-adds all headers. 11. If EnsureSuccessfulResponse, invoke the success guard (BR-CLI-006). - Embedded logic: cast status to int; if < 200 or >= 400, select the best error text and throw a typed SAIFException.

> **Origin**: This step is implemented in [EnsureSuccessfulResponse](ensure-successful-response.md) (`SAIF.Web.API.Clients`) and executed on behalf of this operation.
- On failure: throws a typed `SAIFException`; this operation does not return. When `EnsureSuccessfulResponse` is `false`, failures pass through silently and the caller must inspect `StatusCode`.
  1. Build and return JsonResponse { Content, Headers, StatusCode, SuccessfulRequest = (int)StatusCode < 400 } (BR-CLI-009).
    • Version is never assigned — it stays null and propagates as a null ApiResponse<T>.Version.

Process flow

flowchart TD
    Start(["Verb method invokes<br>GetJsonResponse"]) --> Build["Build RestClient,<br>authenticator, JSON-only<br>handler, timeout"]
    Build --> Request["Build RestRequest,<br>serialize body, add<br>identity headers"]
    Request --> OnBehalf{"SetOnBehalfOfUserHeaders<br>?"}
    OnBehalf -->|Yes| AddHeader["Add OnBehalfOfUser<br>header"] --> Execute
    OnBehalf -->|No| Execute[client.Execute request]
    Execute --> RetryCheck{"Status in<br>RetryOnStatusCodes<br>and retries remain?"}
    RetryCheck -->|Yes| Sleep["Thread.Sleep<br>RetryDelayInMs"] --> Build
    RetryCheck -->|No| Guard{EnsureSuccessfulResponse<br>and status is failure?}
    Guard -->|Yes| Throw([Throw SAIFException])
    Guard -->|No| Return([Return JsonResponse])

Failure and edge-case behavior

  • A null ClientInfo throws NullReferenceException at header-injection time (step 7).
  • SetOnBehalfOfUserHeaders = true outside an ASP.NET request context throws NullReferenceException (step 8).
  • Transport failures (status 0) are retried by default alongside 401/502/503, which retries authentication denials up to 3 times before surfacing them*.
  • Version on the resulting JsonResponse (and therefore on ApiResponse<T>) is always null.

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.Web.API.Clients/JsonHttpClient.cs