Skip to content

Execute JSON API request and return typed envelope

Given a base URI, caller identity, and an authenticator, this operation constructs, executes, retries, and validates an HTTP request to a SAIF Web API, returning a typed ApiResponse<T> envelope or throwing a typed SAIFException subtype.

Field Value
Operation ID OP-01
Trigger Caller invokes GetResponse<T>, PostJson<T>, PutJson<T>, PatchJson<T>, or DeleteJson<T> with a relative URI segment and (for write verbs) a request body object.
Inputs methodUriSegment (string), requestBody (object, write verbs only); instance state _serviceBaseUri, _clientInfo, _authenticator, TimeoutOverride, SetOnBehalfOfUserHeaders, RetryOnStatusCodes, RetryDelayInMs, EnsureSuccessfulResponse, DateTimeZoneHandling.
Outputs ApiResponse<T> (Results, Server, StatusCode, Version) — or a thrown SAIFException subtype.
Primary components JsonHttpClient.GetJsonResponse (private), JsonHttpClient.GetApiResponse<T>
Entry point src/SAIF.Web.API.Clients/JsonHttpClient.cs

Steps

  1. Create the transport client: new RestClient(_serviceBaseUri); assign Authenticator; ClearHandlers(); AddHandler("application/json", () => new JsonDeserializer()) (BR-02).
  2. Apply timeout: if TimeoutOverride > 0, set client.Timeout (BR-03).
  3. Build the request: new RestRequest(methodUriSegment, method); assign request.JsonSerializer = new SAIF.Web.API.Clients.JsonSerializer(DateTimeZoneHandling) (BR-07).
  4. This step delegates request-body serialization to a locally-implemented operation.
  5. Configures a Newtonsoft-based serializer with MissingMemberHandling.Ignore, NullValueHandling.Include, DefaultValueHandling.Include, and the configured DateTimeZoneHandling (BR-21).

Origin: Step 3.1 is implemented in Serialize request body (SAIF.Web.API.Clients.35) and executed on behalf of this operation. 4. Attach body: if body != null, request.AddJsonBody(body), which invokes JsonSerializer.Serialize (see Serialize request body). 5. Add identity headers: Client-Name ← _clientInfo.MachineName; Application-ID ← _clientInfo.ApplicationId; Client-User ← _clientInfo.UserId (BR-01). 6. Add impersonation header (conditional): if SetOnBehalfOfUserHeaders, add OnBehalfOfUser ← HttpContext.Current.DomainlessUser() (BR-04). - In this .35 project, the supporting HttpContextExtensions.DomainlessUser source file is not compiled and System.Web is not referenced, so this branch has no working implementation here.* 7. Execute: client.Execute(request). 8. Retry decision: if retries > 0 and RetryOnStatusCodes contains the returned status, Thread.Sleep(RetryDelayInMs) then recurse with retries - 1 (BR-05). 9. Error gate: if EnsureSuccessfulResponse, call ExceptionHandler.EnsureSuccessfulResponse(response) (BR-06). - This step delegates to a locally-implemented operation. - Evaluates whether the status is outside 200–399 and, if so, maps the response to a typed exception.

Origin: Step 9.1 is implemented in Translate HTTP failure to typed exception (SAIF.Web.API.Clients.35) and executed on behalf of this operation. - On failure: an exception is thrown and propagates to the caller; steps 10–12 are skipped. 10. Wrap raw response: build JsonResponse { Content, Headers, StatusCode, SuccessfulRequest = (int)StatusCode < 400 }. 11. Deserialize payload: GetApiResponse<T> → GetContentArray<T>(content, DateTimeZoneHandling) into Results (BR-08, BR-09). 12. Populate envelope metadata: Server ← first header named Server-Name; StatusCode ← jsonResponse.StatusCode; Version ← jsonResponse.Version (BR-10).

Process flow

The following diagram visualizes this operation's control flow.

flowchart TD
    Start([Verb method invoked]) --> S1["Create RestClient and<br>RestRequest"]
    S1 --> S2["Add identity and<br>impersonation headers"]
    S2 --> S3[Execute request]
    S3 --> D{"Retryable status and<br>retries remain?"}
    D -->|Yes| Retry["Sleep RetryDelayInMs,<br>retry"]
    Retry --> S3
    D -->|No| Gate{"EnsureSuccessfulResponse<br>and status outside<br>200-399?"}
    Gate -->|Yes| EndErr(["Throw typed<br>SAIFException"])
    Gate -->|No| Deserialize["Deserialize payload into<br>ApiResponse"]
    Deserialize --> EndOk([Return ApiResponse T])

Failure and edge-case behavior

  • An exception thrown at step 9 propagates to the caller; steps 10–12 are skipped.
  • Retry at step 8 happens before the error gate, so retryable failures (502/503/401/transport-failure 0) are re-attempted up to 3 times before any exception is raised.
  • ApiResponse<T>.Version is never populated by this library: GetApiResponse<T> copies jsonResponse.Version, but GetJsonResponse never assigns JsonResponse.Version when constructing the object, so the value 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
  • src/SAIF.Web.API.Clients/JsonResponse.cs