Table of Contents

Executing requests and result types

Once a request is configured, you execute it and collect the response in one of three shapes: text, a stream, or a file. Each shape has a matching execute method and a matching ResultType. Execution is asynchronous by default, so the call returns immediately and the response is delivered through a callback or event. This chapter explains the execution modes and the three result types, and how to chain one call into the next.

Synchronous and asynchronous execution

Async defaults to True: the execute method returns at once and the response arrives later in a callback or response event — the right choice for UI applications, where blocking the main thread would freeze the window. Set Async := False only in console tools or on a background thread; the executed request then already holds the response when the call returns.

procedure TForm1.ExecuteAsyncOrSync;
var
  LExecuted: TTMSFNCRESTClientExecutedRequest;
begin
  TMSFNCRESTClient1.Request.URL := 'https://jsonplaceholder.typicode.com/posts/1';

  { Async (the default, True) returns immediately; read the result in a
    callback or response event. Keep this for UI applications. }
  TMSFNCRESTClient1.Request.Async := True;
  TMSFNCRESTClient1.ExecuteRequestWithResultString(
    procedure(AResponseString: string)
    begin
      Memo1.Lines.Text := AResponseString;
    end);

  { Async := False blocks until the response is in. The returned executed
    request already holds the response, so no callback is needed. Use it
    only off the main thread or in console/tool code. }
  TMSFNCRESTClient1.Request.Async := False;
  LExecuted := TMSFNCRESTClient1.ExecuteRequestWithResultString;
  Memo1.Lines.Text := LExecuted.Response.ResponseString;
end;
Warning

Never use Async := False on the main UI thread. A slow or unreachable server will hang the application until the request times out.

Reading a text response

For JSON, XML, or any text payload, set ResultType := rrtString and call ExecuteRequestWithResultString. You can pass an inline callback that receives the body, in addition to the design-time event.

procedure TForm1.ReadStringResult;
begin
  TMSFNCRESTClient1.Request.ResultType := rrtString;
  TMSFNCRESTClient1.Request.URL := 'https://jsonplaceholder.typicode.com/posts/1';

  { Pass a callback directly to the execute method. It runs when the
    response arrives, in addition to any OnRequestResponseStringRetrieved
    handler assigned at design time. }
  TMSFNCRESTClient1.ExecuteRequestWithResultString(
    procedure(AResponseString: string)
    begin
      Memo1.Lines.Text := AResponseString;
    end);
end;

Downloading into a stream

For binary content — images, archives, anything you keep in memory — set ResultType := rrtStream and call ExecuteRequestWithResultStream. The callback receives a TMemoryStream ready to load.

procedure TForm1.DownloadImage(const AImageURL: string);
begin
  TMSFNCRESTClient1.Request.Clear;
  TMSFNCRESTClient1.Request.ResultType := rrtStream;
  TMSFNCRESTClient1.Request.URL := AImageURL;

  { The stream callback receives a TMemoryStream positioned at the start of
    the response body, ready to load. }
  TMSFNCRESTClient1.ExecuteRequestWithResultStream(
    procedure(AResponseStream: TMemoryStream)
    begin
      Image1.Bitmap.LoadFromStream(AResponseStream);
    end);
end;

Saving a response to a file

To write a download straight to disk without holding it in memory, set ResultType := rrtFile, set ResultFile to the destination path, and call ExecuteRequestWithResultFile. The callback reports the final path and byte count.

procedure TForm1.DownloadToFile;
begin
  TMSFNCRESTClient1.Request.Clear;
  TMSFNCRESTClient1.Request.ResultType := rrtFile;

  { ResultFile is the destination path the response body is written to. }
  TMSFNCRESTClient1.Request.ResultFile := TPath.Combine(TPath.GetDownloadsPath, 'report.pdf');
  TMSFNCRESTClient1.Request.URL := 'https://api.example.com/reports/42.pdf';

  TMSFNCRESTClient1.ExecuteRequestWithResultFile(
    procedure(AResponseFile: string; AResponseFileSize: Integer)
    begin
      ShowMessage(Format('Saved %d bytes to %s', [AResponseFileSize, AResponseFile]));
    end);
end;
Note

The plain ExecuteRequest honours whatever ResultType you set on the request, and its OnRequestResponseRetrieved event exposes all three targets (string, stream, and file) at once. The typed ExecuteRequestWith... methods are shortcuts that also set the matching ResultType for you.

Chaining requests together

Real workflows often run one request based on another's result — fetch a record, then download an image it references. The example combines a text request and a stream request: it parses the first JSON response, extracts the thumbnailUrl, and feeds it into a second, stream-based call.

procedure TForm1.LoadPhotoThumbnail(APhotoID: Integer);
begin
  { Step 1 - a string request returns a JSON record. }
  TMSFNCRESTClient1.Request.Clear;
  TMSFNCRESTClient1.Request.ResultType := rrtString;
  TMSFNCRESTClient1.Request.Host := 'https://jsonplaceholder.typicode.com';
  TMSFNCRESTClient1.Request.Path := '/photos/' + IntToStr(APhotoID);

  TMSFNCRESTClient1.ExecuteRequestWithResultString(
    procedure(AResponseString: string)
    var
      LJSON: TJSONValue;
      LImageURL: string;
    begin
      LJSON := TTMSFNCUtils.ParseJSON(AResponseString);
      if not Assigned(LJSON) then
        Exit;
      try
        LImageURL := TTMSFNCUtils.GetJSONProp(LJSON, 'thumbnailUrl');
        { Step 2 - feed the extracted URL into a stream request. }
        if LImageURL <> '' then
        begin
          TMSFNCRESTClient1.Request.Clear;
          TMSFNCRESTClient1.Request.ResultType := rrtStream;
          TMSFNCRESTClient1.Request.URL := LImageURL;
          TMSFNCRESTClient1.ExecuteRequestWithResultStream(
            procedure(AResponseStream: TMemoryStream)
            begin
              Image1.Bitmap.LoadFromStream(AResponseStream);
            end);
        end;
      finally
        LJSON.Free;
      end;
    end);
end;

Common mistakes

  • Result type and execute method disagree. Calling ExecuteRequestWithResultStream while ResultType is rrtString gives you an empty stream. Use the typed method (which sets the type) or set ResultType to match.
  • rrtFile without ResultFile. A file result with no destination path has nowhere to write. Always set ResultFile first.
  • Reading the result on the wrong thread. With async execution, touch UI controls only inside the callback or response event, which run on the main thread — not right after the execute call returns.

See also