---
title: GetJsonResponse
summary: Shared HTTP request funnel used by every public verb method on JsonHttpClient.
status: draft
owner: bendy
last_reviewed: 2026-09-15
library: SAIF.Web.API.Clients
reference_type: package
operation_id: OP-05
trigger: Every public verb method; also re-entrant on retry
---

# 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](../business-rules.md#br-cli-001-ntlm-default-authentication)).
3. `client.ClearHandlers()` then `client.AddHandler("application/json", () => new JsonDeserializer())` — replaces all default content handlers so only `application/json` is deserializable ([BR-CLI-015](../business-rules.md#br-cli-015-json-only-content-handling)).
   - If the response's `Content-Type` is not JSON, no deserialized object is produced by RestSharp; the raw `Content` string is still available downstream.
4. If `TimeoutOverride > 0`, set `client.Timeout` ([BR-CLI-010](../business-rules.md#br-cli-010-per-client-request-timeout)).
   - If `TimeoutOverride` is `0`, the platform `HttpWebRequest` default remains in force.
5. Build `new RestRequest(methodUriSegment, method)` and assign `request.JsonSerializer = new JsonSerializer(DateTimeZoneHandling)` ([BR-CLI-011](../business-rules.md#br-cli-011-date-time-zone-handling-consistency)).
   - Embedded logic: construct a `Newtonsoft.Json.JsonSerializer` with the configured date-time-zone handling.

   > **Origin**: This step is implemented in [Serialize (JsonSerializer)](serialize-request-body.md) (`SAIF.Web.API.Clients`) and executed on behalf of this operation.
6. If `body != null`, `request.AddJsonBody(body)` ([BR-CLI-011](../business-rules.md#br-cli-011-date-time-zone-handling-consistency)).
   - 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](../business-rules.md#br-cli-002-caller-identity-headers-required)).
   - 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](../business-rules.md#br-cli-003-on-behalf-of-user-forwarding), [BR-CLI-004](../business-rules.md#br-cli-004-forwarded-user-name-normalization)).
   - Embedded logic: strip the `DOMAIN\` prefix or `@domain` suffix from the ambient user's identity name.

   > **Origin**: This step is implemented in [DomainlessUser](domainless-user.md) (`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](../business-rules.md#br-cli-005-transient-failure-retry)).
    - 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](../business-rules.md#br-cli-006-non-success-response-failure-conversion)).
    - 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`.
12. Build and return `JsonResponse { Content, Headers, StatusCode, SuccessfulRequest = (int)StatusCode < 400 }` ([BR-CLI-009](../business-rules.md#br-cli-009-response-success-flag)).
    - `Version` is never assigned — it stays `null` and propagates as a `null` `ApiResponse<T>.Version`.

## Process flow

```mermaid
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`.

## Related documentation
- [Operations and processes](../operations-processes.md)
- [Business rules](../business-rules.md)

> **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`
