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¶
- Construct
new RestClient(_serviceBaseUri). A new client is created per call — there is no connection/handler reuse across calls*. - Assign
client.Authenticator = _authenticator(NTLM by default) (BR-CLI-001). client.ClearHandlers()thenclient.AddHandler("application/json", () => new JsonDeserializer())— replaces all default content handlers so onlyapplication/jsonis deserializable (BR-CLI-015).- If the response's
Content-Typeis not JSON, no deserialized object is produced by RestSharp; the rawContentstring is still available downstream. - If
TimeoutOverride > 0, setclient.Timeout(BR-CLI-010). - If
TimeoutOverrideis0, the platformHttpWebRequestdefault remains in force. - Build
new RestRequest(methodUriSegment, method)and assignrequest.JsonSerializer = new JsonSerializer(DateTimeZoneHandling)(BR-CLI-011). - Embedded logic: construct a
Newtonsoft.Json.JsonSerializerwith 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. Ifbody != null,request.AddJsonBody(body)(BR-CLI-011). - Ifbodyisnull, no body is added — valid for GET or a body-less DELETE. 7. Add headersClient-Name=_clientInfo.MachineName,Application-ID=_clientInfo.ApplicationId,Client-User=_clientInfo.UserId(BR-CLI-002). - On failure:_clientInfois not null-checked — anullClientInfothrowsNullReferenceExceptionhere. Individualnullproperty values are added as-is. 8. IfSetOnBehalfOfUserHeaders, add headerOnBehalfOfUser=HttpContext.Current.DomainlessUser()(BR-CLI-003, BR-CLI-004). - Embedded logic: strip theDOMAIN\prefix or@domainsuffix 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.NETHttpContext; anullcontext throwsNullReferenceException. 9.client.Execute(request)— synchronous, blocking. - On failure: transport failure does not throw here; RestSharp returns status0withErrorExceptionpopulated. 10. Ifretries > 0andRetryOnStatusCodescontains the response status:Thread.Sleep(RetryDelayInMs), then recurse withretries - 1(BR-CLI-005). - Default budget is 3 retries (4 total attempts) at 1000 ms each. Status0(transport failure),401,502,503are retried by default. The retried call rebuilds the client and re-adds all headers. 11. IfEnsureSuccessfulResponse, invoke the success guard (BR-CLI-006). - Embedded logic: cast status toint; if< 200or>= 400, select the best error text and throw a typedSAIFException.
> **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`.
- Build and return
JsonResponse { Content, Headers, StatusCode, SuccessfulRequest = (int)StatusCode < 400 }(BR-CLI-009).Versionis never assigned — it staysnulland propagates as anullApiResponse<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
nullClientInfothrowsNullReferenceExceptionat header-injection time (step 7). SetOnBehalfOfUserHeaders = trueoutside an ASP.NET request context throwsNullReferenceException(step 8).- Transport failures (status
0) are retried by default alongside401/502/503, which retries authentication denials up to 3 times before surfacing them*. Versionon the resultingJsonResponse(and therefore onApiResponse<T>) is alwaysnull.
Related documentation¶
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