Table of Contents

Responses, events, and history

After a request runs, the client gives you several ways to react: response events that fire as each call completes, a rich Response object with status, size, timing, and headers, and an ExecutedRequests collection that records every call. This chapter shows how to wire the events, read the full response metadata, and walk the history.

Response events

The client raises one response event per result shape: OnRequestResponseStringRetrieved, OnRequestResponseStreamRetrieved, OnRequestResponseFileRetrieved, and the all-in-one OnRequestResponseRetrieved. Each handler receives the TTMSFNCRESTClientExecutedRequest that produced it, so a single handler can serve many endpoints. Two more events fire while a request is being built: OnRequestURLChanged (and lets you adjust the URL) and OnRequestHeadersChanged.

procedure TForm1.FormCreate(Sender: TObject);
begin
  { Assign the event in code, or pick it in the Object Inspector. The event
    fires for every request whose ResultType is rrtString. }
  TMSFNCRESTClient1.OnRequestResponseStringRetrieved := HandleStringResponse;
end;

procedure TForm1.HandleStringResponse(Sender: TObject;
  ARequest: TTMSFNCRESTClientExecutedRequest; AResultString: string);
begin
  { ARequest links the response to the request that produced it, so a single
    handler can serve many endpoints. }
  if ARequest.Response.ResponseSuccess then
    Memo1.Lines.Text := AResultString
  else
    Memo1.Lines.Text := Format('HTTP %d', [ARequest.Response.ResponseCode]);
end;

Inspecting the response

Each executed request exposes a Response object. Use it to branch on the outcome and to surface diagnostics: ResponseSuccess and ResponseCode for status, ResponseString for the body, ResponseSize and ResponseTime for diagnostics, and ResponseHeaders for the server's headers.

procedure TForm1.ShowResponseDetails(ARequest: TTMSFNCRESTClientExecutedRequest);
var
  LResponse: TTMSFNCRESTClientResponse;
begin
  LResponse := ARequest.Response;

  Memo1.Lines.Clear;
  Memo1.Lines.Add(Format('Success : %s', [BoolToStr(LResponse.ResponseSuccess, True)]));
  Memo1.Lines.Add(Format('Status  : %d', [LResponse.ResponseCode]));
  Memo1.Lines.Add(Format('Size    : %d bytes', [LResponse.ResponseSize]));
  Memo1.Lines.Add(Format('Time    : %d ms', [LResponse.ResponseTime]));

  { ResponseHeaders is a key/value collection returned by the server. }
  Memo1.Lines.Add('Headers :');
  Memo1.Lines.Add(LResponse.ResponseHeaders.GetHeadersText);
end;
Member Meaning
ResponseSuccess True when the low-level request reported success.
ResponseCode The HTTP status code returned by the server.
ResponseString The response body as text.
ResponseSize The body size in bytes.
ResponseTime Elapsed server round-trip time in milliseconds.
ResponseHeaders The headers returned by the server, as a key/value collection.

Reviewing the execution history

Every call appends an entry to ExecutedRequests. Iterate it to audit a batch, or call GetExecutedRequestByURL to fetch the most recent execution for a given URL. Each entry also carries application-defined slots (DataString, DataInteger, DataObject, and more) you can use to tie a call back to its originating context.

procedure TForm1.ReviewHistory;
var
  I: Integer;
  LExecuted: TTMSFNCRESTClientExecutedRequest;
begin
  { Every call appends an entry to ExecutedRequests, so you can audit or
    retry previous calls. }
  for I := 0 to TMSFNCRESTClient1.ExecutedRequests.Count - 1 do
  begin
    LExecuted := TMSFNCRESTClient1.ExecutedRequests[I];
    Memo1.Lines.Add(Format('%s -> HTTP %d',
      [LExecuted.Request.URL, LExecuted.Response.ResponseCode]));
  end;

  { Or fetch the most recent execution for a specific URL. }
  LExecuted := TMSFNCRESTClient1.GetExecutedRequestByURL('https://jsonplaceholder.typicode.com/posts/1');
  if Assigned(LExecuted) then
    ShowMessage(LExecuted.Response.ResponseString);
end;

Attaching custom data with DataUpload and the data slots

Because execution is asynchronous, you often need to know which application object a response belongs to. Each TTMSFNCRESTClientExecutedRequest carries application-defined slots for exactly this: DataString, DataInteger, DataObject, DataPointer, and DataBoolean for arbitrary context, plus DataUpload to associate a file with the call (a file path on VCL and FMX) — handy when tracking an rmPOSTMULTIPART upload. Tag the executed request when you start the call, then read the slot back in the response event.

procedure TForm1.TagExecution;
var
  LExecuted: TTMSFNCRESTClientExecutedRequest;
begin
  TMSFNCRESTClient1.Request.URL := 'https://api.example.com/v1/upload';
  LExecuted := TMSFNCRESTClient1.ExecuteRequestWithResultString;

  { Every executed request carries application-defined slots you can use to
    correlate an async response back to its originating context. }
  LExecuted.DataString := 'order-4711';
  LExecuted.DataInteger := CurrentUserID;
  LExecuted.DataObject := SelectedOrder;

  { DataUpload associates a file with the execution - on VCL and FMX it is a
    file path, useful when tracking an rmPOSTMULTIPART upload. }
  LExecuted.DataUpload := 'C:\invoices\invoice.pdf';
end;

Combining events with history

The two work well together: an event handler logs each response as it arrives, and the history gives you a total once the batch finishes.

procedure TForm1.RunBatch;
begin
  { One handler records every response as it arrives. }
  TMSFNCRESTClient1.OnRequestResponseStringRetrieved := LogResponse;

  TMSFNCRESTClient1.Request.URL := 'https://jsonplaceholder.typicode.com/posts/1';
  TMSFNCRESTClient1.ExecuteRequestWithResultString;

  TMSFNCRESTClient1.Request.URL := 'https://jsonplaceholder.typicode.com/posts/2';
  TMSFNCRESTClient1.ExecuteRequestWithResultString;
end;

procedure TForm1.LogResponse(Sender: TObject;
  ARequest: TTMSFNCRESTClientExecutedRequest; AResultString: string);
begin
  Memo1.Lines.Add(Format('%s -> HTTP %d in %d ms',
    [ARequest.Request.URL, ARequest.Response.ResponseCode, ARequest.Response.ResponseTime]));
end;

procedure TForm1.SummarizeRun;
begin
  { After the batch, the full history is available for an audit total. }
  Label1.Text := Format('%d request(s) executed', [TMSFNCRESTClient1.ExecutedRequests.Count]);
end;

Common mistakes

  • Assuming an HTTP error raises an exception. A 404 or 500 still completes the request; check ResponseSuccess and ResponseCode rather than relying on a try/except.
  • Letting the history grow unbounded. Long-running apps accumulate ExecutedRequests entries. Call Clear periodically to release them.
  • Touching freed streams. The stream passed to a response event is valid only for the duration of the handler; copy what you need before it returns.

See also