Blog

All Blog Posts  |  Next Post  |  Previous Post

Diving deeper: Cloudbase REST in Delphi, Part 1/3: Basics

Bookmarks: 

Thursday, September 22, 2022

TMS Software Delphi  Components

Intro

TMS FNC Core is a universal core layer for creating rich visual and non-visual components for VCL, FMX, LCL and WEB core apps. A major part of TMS FNC Core is the ability to create and execute REST requests.

The "Diving Deeper: Cloudbase REST" blog series will consist out of three parts:

  1. Basics, getting to know Cloudbase REST
  2. Extended, various special REST specific requirements and how to implement them 
  3. Sync vs Async operations

The blog series will use a test REST service. Note that when referring to unit names, the prefix of the framework ((FMX.)(VCL.)(WEBLib.)(LCL)) will not be included, for readability purposes. The code snippets are written in FMX as a default framework, but can easily be ported to other frameworks. If you have any questions during the blog series, don't hesitate to ask them in the comments section, or by using our support channels.

Units

After installing TMS FNC Core, the most important units are the TMSFNCCloudBase.pas and the TMSFNCUtils.pas unit.

  • TMSFNCCloudBase.pas: Unit containing interfaces and functions to create and execute requests, and to capture and parse the result.
  • TMSFNCUtils.pas: Unit containing a lot of class helper functions which, in some situations, are helpful to build the request with proper encoding, or to easily convert data to a specific type required for the REST request to be executed. Additionally, this unit contains class wrappers to parse JSON, which in many situations is the result string from the request.

Getting Started

We get started by adding the unit TMSFNCCloudBase. There is a class called TTMSFNCCloudBase, that provides the required functions and properties to setup a REST Request. To create an instance of TTMSFNCCloudBase, call

c := TTMSFNCCloudBase.Create;
To access and set the request data before sending it, use the following properties. Note that the data is not real, it's to show the capabilities of the request. The next chapter sample code will include a real service with real data.

c.Request.Clear;
c.Request.Host := 'https://myhost.com';
c.Request.Method := rmGET;
c.Request.Path := '/mypath/upload';
c.Request.Query := 'param1=value&param2=value';
c.Request.PostData := 'MyPostData';
c.Request.ResultType := rrtString; //rrtStream //rrtFile
c.Request.AddHeader('Header1', 'Header Data');
To execute a request call

c.ExecuteRequest(
  procedure(const ARequestResult: TTMSFNCCloudBaseRequestResult)
  begin
    //Parse ARequestResult
  end
  );
Note that the request result is captured in an anonymous callback, which is asynchronous by default. The last chapter will cover this and show the difference between synchronous and asynchronous requests. Note that the above code is going to generate a memory leak. Always make sure to destroy the instance of TTMSFNCCloudBase at some point in your application. The above calls are not taking this into account. The best practice is to create an instance when the application is starting, and destroy it when the application is closing. This way, you can create and manage multiple requests with one instance during the lifetime of your application.

After executing the request, the callback is triggered. The ARequestResult of type TTMSFNCCloudBaseRequestResult will contain the result of the request. There are 3 types of result request content. The request result type needs to be set before executing the request.

  • rrtString: The default return value of the request result. Can be XML, JSON, or any other type of text. The property ARequestResult.ResultString contains the value.
  • rrtStream: Returns the content as a stream, the property ARequestResult.ResultStream contains the content
  • rrtFile: Immediately saves the content to a file, specified by c.Request.ResultFile before executing the request.

Supported Resource Methods

Below is a list of supported resource methods in TTMSFNCCloudBase

  • rmGET: The GET method is used to read/retrieve the content of a resource.
  • rmPOST: The POST method is most-often utilized to create new resources.
  • rmPOSTMULTIPARTRELATED, 
  • rmPOSTMULTIPART: Same as POST, but with a special multi-part post data body for sending special request content such as images, files or any other binary content.
  • rmPUT: PUT is most-often utilized for updating resources.
  • rmPUTMULTIPART, 
  • rmPUTMULTIPARTRELATED: Same as PUT, but with a special multi-part post data, similar to POST multipart.
  • rmDELETE: The DELETE method is used to delete a resource.
  • rmPATCH: The PATCH method is used to modify a resource, mostly an incremental difference between the original resource and the new resource.
  • rmUPDATE: An equivalent for the rmPUT resource method.

For testing purposes we use httpbin.org which is a service to test REST requests. The first part of this blog post is not going to cover all of the above resource methods. In this blog post we are going to cover the GET and POST methods and the next blog post will go a bit deeper and will cover a couple of special cases.

GET

To execute a GET request we use the following code.

c.Request.Clear;
c.Request.Host := 'https://httpbin.org';
c.Request.Method := rmGET;
c.Request.Path := '/get';
c.Request.ResultType := rrtString;
c.ExecuteRequest(
  procedure(const ARequestResult: TTMSFNCCloudBaseRequestResult)
  begin
    if ARequestResult.Success then
      Memo1.Text := ARequestResult.ResultString
    else
      Memo1.Text := 'Request failed';
  end
  );
To check if the result is actually successfully returned from the service, we can check the ARequestResult.Success boolean which actually internally maps on ARequestResult.ResponseCode and verifies if the code is a valid HTTP response code. To learn more about the various response codes, visit https://developer.mozilla.org/en-US/docs/Web/HTTP/Status. In most cases, the ARequestResult.ResultString will also contain a reason why the request failed, paired with a response code. This can typically be one of the following reasons:

  • Credentials are missing such as API key, client-id & secret which are required for services that require authentication
  • URL is malformed
  • Data is not correctly formatted or is missing required parameters
  • ...

When executing the request, and the request succeeds, we get back JSON. the JSON contains information about the request.

{
  "args": {}, 
  "headers": {
    "Cache-Control": "no-cache", 
    "Host": "httpbin.org", 
    "User-Agent": "Mozilla/5.001 (windows; U; NT4.0; en-US; rv:1.0) Gecko/25250101", 
    "X-Amzn-Trace-Id": "Root=1-63246a5a-341eb67801e5c0f00897996f"
  }, 
  "origin": "X", 
  "url": "https://httpbin.org/get"
}

For example, if we would change the request to include query parameters like the code below:

c.Request.Clear;
c.Request.Host := 'https://httpbin.org';
c.Request.Method := rmGET;
c.Request.Path := '/get';
c.Request.Query := 'param1=Hello%20World&param2=My%20First%20Request';
c.Request.ResultType := rrtString;
c.ExecuteRequest(
  procedure(const ARequestResult: TTMSFNCCloudBaseRequestResult)
  begin
    if ARequestResult.Success then
      Memo1.Text := ARequestResult.ResultString
    else
      Memo1.Text := 'Request failed';
  end
  );
The response is

{
  "args": {
    "param1": "Hello World", 
    "param2": "My First Request"
  }, 
  "headers": {
    "Cache-Control": "no-cache", 
    "Host": "httpbin.org", 
    "User-Agent": "Mozilla/5.001 (windows; U; NT4.0; en-US; rv:1.0) Gecko/25250101", 
    "X-Amzn-Trace-Id": "Root=1-63246d6a-00d0a592795a4fcd73753f6d"
  }, 
  "origin": "X", 
  "url": "https://httpbin.org/get?param1=Hello World&param2=My First Request"
}
as you can see, the parameters that are passed as a query parameter to the URL are retrieved, parsed and used as parameters for the request. Note that we have to properly encode the URL. In TMSFNCUtils, there is a utility function available to that so you won't have to worry about proper encoding. To apply this to our request query property we change

c.Request.Query := 'param1=Hello%20World&param2=My%20First%20Request';

to

c.Request.Query := 'param1=' + TTMSFNCUtils.URLEncode('Hello World') + '&param2=' + TTMSFNCUtils.URLEncode('My First Request');

POST

As explained a POST request is sending content to the service. For httpbin.org we can send any content we want, but other services typically require specific data information and in a particular format. To send data to the service we use the PostData property. Below is an example

c.Request.Clear;
c.Request.Host := 'https://httpbin.org';
c.Request.Method := rmPOST;
c.Request.Path := '/post';
c.Request.PostData := '{"MyData":"Hello World"}';
c.Request.ResultType := rrtString;
c.ExecuteRequest(
  procedure(const ARequestResult: TTMSFNCCloudBaseRequestResult)
  begin
    if ARequestResult.Success then
      Memo1.Text := ARequestResult.ResultString
    else
      Memo1.Text := 'Request failed';
  end
  );
The result we get back is

{
  "args": {}, 
  "data": "{\"MyData\":\"Hello World\"}", 
  "files": {}, 
  "form": {}, 
  "headers": {
    "Cache-Control": "no-cache", 
    "Content-Length": "24", 
    "Host": "httpbin.org", 
    "User-Agent": "Mozilla/5.001 (windows; U; NT4.0; en-US; rv:1.0) Gecko/25250101", 
    "X-Amzn-Trace-Id": "Root=1-632473d8-5d71c7d1106087a277a7713b"
  }, 
  "json": {
    "MyData": "Hello World"
  }, 
  "origin": "X", 
  "url": "https://httpbin.org/post"
}
We clearly see that the JSON data we sent is accepted by the service and the response shows the received data.

Helper Methods

In the cloud base unit there are a couple of helper methods/functions available to easily setup a rest request. This technique can be used to download a file, or request data with a simple request to an URL. Below is a list of methods or functions that can be used to achieve this.

function HTTPPostDataBuilder: TTMSFNCCloudBaseRequestPostDataBuilder;
procedure HTTPClearHeaders;
procedure HTTPAddHeader(const AName: string; const AValue: string);
procedure HTTPCloudRequest(const AURL: string; AResultType: TTMSFNCCloudBaseRequestResultType = rrtString; AMethod: TTMSFNCCloudBaseRequestMethod = rmGET; const ARequestResultEvent: TTMSFNCCloudBaseRequestResultEvent = nil); overload;
procedure HTTPCloudRequest(const AHost, APath, AQuery, APostData: string; AResultType: TTMSFNCCloudBaseRequestResultType = rrtString; AMethod: TTMSFNCCloudBaseRequestMethod = rmPOST; const ARequestResultEvent: TTMSFNCCloudBaseRequestResultEvent = nil); overload;
With the above methods, you can basically prepare the headers and postdata and then call the request in one line. Internally, the HTTPCloudRequest method will asynchronously build and execute the request and trigger the callback when finished.

TTMSFNCCloudBase.DownloadFileFromURL('https://www.myserver.com/myfile.zip',
procedure(const ARequestResult: TTMSFNCCloudBaseRequestResult)
begin
  ARequestResult.ResultStream.SaveToFile('myfile.zip');
end);

The class method DownloadFileFromURL will download the file specified as a parameter to a memory stream (ARequestResult.ResultStream) and then allow to save it to a file.

Feedback

Next up will be a more extended guide around REST and TTMSFNCCloudBase, so stay tuned for more to come! As always, please leave a comment or if you have any questions, don't hesitate to ask us!



Pieter Scheldeman


Bookmarks: 

This blog post has not received any comments yet.



Add a new comment

You will receive a confirmation mail with a link to validate your comment, please use a valid email address.
All fields are required.



All Blog Posts  |  Next Post  |  Previous Post