REST Client Library

From RAD Studio
Jump to: navigation, search

Go Up to DataSnap REST


The Embarcadero REST Library is a framework for accessing REST-based web services (REST stands for Representational State Transfer). The library is available for all platforms that are supported by Delphi.

The framework focuses on JSON as the representation format. XML is not explicitly supported.


Terms

Term Definition

REST

REpresentational State Transfer - as defined by Roy Fielding, 2000

Service

A REST-based web service

Client

An instance of the TRESTClient component

Request

An instance of the TRESTRequest component

Response

An instance of the TRESTResponse component

Main Components and Classes

The concept of the REST components is built around a troika of three main components: the request, the client, and the response. All of them are part of the workflow when fetching data from a service.

TRESTClient

REST.Client.TRESTClient is the component that actually executes a request to a service. TRESTClient manages the HTTP connection to the service, deals with HTTP headers and proxy servers, and receives the response data. Authenticators (described later) can be attached to the client for a convenient way of adding authentication to a request.

One of the most important properties of the client is the BaseURL. The base-url is the primary endpoint of a service-provider and typically remains constant for all requests to this provider. All requests are directed to this endpoint, so the base-url is the 'first half' of the complete request-url. The 'second half' is the 'resource', and it is provided by the request-component. The documentation of the service-provider typically contains information about the url-structure.

TRESTClient is built to hide any HTTP-related exceptions as long as the exception can be represented by a standard HTTP code. The client only throws 'hard' exceptions, connection failures, and so on. The framework operates on the newer TNetHTTPClient, which is based on system HTTP client sockets. This allows it to leverage the OS's ability to automatically resolve and deal with SSL/TLS/HTTPS.

TRESTRequest

The request (REST.Client.TRESTRequest) holds all parameters and settings that form the actual HTTP request to the service. When connected to a client-component, it can be executed (even at design time). One of the most important properties of the request is the 'Resource'. This value forms the previously mentioned 'second half' of the full request-url and defines the actual action that is executed on the service. The request also defines the HTTP methods ('get', 'post', 'put', 'delete') that are used for its execution.

Typically, a request contains parameters such as the following:

Parameters

The TRESTRequest component supports several different types of parameters, all of which have their own usage pattern.

Header Parameters

These parameters have the form of key=value and are embedded into the HTTP header of the request.

Post/Get Parameters

These parameters have the form of key=value, and their integration into the request depends on the request method.

  • For HTTP post requests, the parameters are embedded into the body of the request.
  • For HTTP get requests, the parameter is embedded into the URL as part of the query string.
Note: When using these parameters avoid the curly brackets in [ResourceSuffix('item')] parameter.

Body Parameters

These parameters consist only of a value and are integrated into the body of the request. They cannot be used for HTTP get or HTTP delete requests since they do not have a body.

URL Segment Parameters

URL Segment Parameters are positional based and can be found in each URL segment. For example

 http://www.example.com/first-segment/second-segment/index.html?

These parameters can be created automatically based on the value entered for BaseURL and Request. You can use the property AutoCreateParams to control this behavior.

Note: The parameter value depends on its kind, which is a specific flag.

Cookie-Parameters

These parameters have the form of key=value, and are sent as cookie-values.

All parameters are encoded automatically to match the requirements of a valid HTTP request per default. There might be scenarios in which the value of the parameter is already encoded and does not need to be encoded again. In this case, set the flag 'poDoNotEncode' in the parameter’s options, which forces the request to take the parameter value as it is.

REST.Client.TRESTResponse

The response holds all the returned data from the service. The data includes the HTTP status code, error messages (if any exist), and of course the returned JSON data. A response object can be created on the fly by the client while executing a request, or the response object can be created beforehand and then connected to a client before executing a request.

The response-data can be accessed using one of the 'Content', 'JSONValue', or 'RAWbytes' property.

Authentication

Most services require an authentication before they can be used. The authenticator classes are used to apply the specific authentication methods that are required by the REST service. An authenticator is attached to the client and is automatically executed on each executed request. After the property is configured, it can be used for as many requests as needed, without touching them again. Typically, the authenticator works as a decorator for the request by adding headers or other parameters to it. Because of the variety of existing authentication methods, it would not be sufficient to provide only a single authenticator. A flexible framework was created instead, allowing to authenticate against a lot of different methods.

A couple of standard authenticators are included in the component set, and custom authenticators can easily be integrated (see below).

TSimpleAuthenticator

The simple authenticator mimics a basic authentication like an HTML form containing an input field for a username and another one for a password. The data is transmitted as a tuple of key=value where value comes first, the username, and then the password. Next to the username and the password itself, this authenticator also requires the names of the keys. The value for a username could be transmitted as 'user', 'username', or maybe as simple as 'u'. These key names can be specified in the UserNameKey and PasswordKey properties.

Depending on the method of the request, the data is transmitted either in the URL for a get request (unsecure) or in the body for a post request.

THTTPBasicAuthenticator

The basic authenticator addresses the HTTP basic authentication. It embeds a value containing the username and password into the HTTP header of the request. As RFC2617 defines them, the username and the password are base64-encoded. The header is embedded for each request.

TOAuth1Authenticator/ TOAuth2Authenticator

The OAuth1-Authenticator assists in the support for authentication using the 'OAuth 1.0a' method.

Please be aware that OAuth1 is a workflow including user interaction. This means that supporting OAuth1 authentication requires more work than just plugging in the authenticator. The idea of OAuth is that the application brings up a web view to the user, showing the login page of the service provider (such as Twitter, for example). The application itself never gets access to the username or password of the user; it just receives tokens from the service provider, and it has to use these tokens to access the service.

Some services (such as Twitter or Google, see OAuth2) allow storing these access tokens to reuse them later, to avoid another explicit authentication by the user. However, this behavior is absolutely service-dependent. Other services might discard the tokens after a (short) period of time.

Because of the heavy dependencies to the service providers, a generic component like this can only give some support and provide the infrastructure to follow the workflow of the service provider. You might want to inherit from this class to create an authenticator class specific to a service provider.

Please see an example for this authenticator in the Examples: Twitter, Google Tasks, Facebook.

Creating Custom Authenticators

If none of the existing authenticators works for a special service, custom authenticators can be created. The first step is to create a new class, inheriting from TCustomAuthenticator, and to overwrite the DoAuthenticate method. This method takes two parameters: a reference to the client executing the current request and a reference to the request itself.

Parameters can be added to the request as needed.

Additional Classes

In addition to the main components request, client, and response, the library also contains a dataset adapter allowing to transfer a JSON formed response into a dataset descendant class.

TRESTResponseDataSetAdapter

When a rest service response is in JSON format, a RESTResponseDataSetAdapter can parse the response content and transfer the data into a TDataSet.

Please be aware that not every JSON data can be transformed into a DataSet structure. The parser is looking for a JSON object or an array of JSON objects. The parser looks for the collections of the key values of the JSON objects to create the dataset columns. Due to the nature of JSON data, the parser iterates through the whole response and collects information for the columns of the dataset. This way, the dataset is always complete.

The adapter is only for displaying data. To allow editing of the data and posting them back to the server, you need to create your own adapter.

Predefined Fields

Similar to a database-related dataset, this dataset adapter can contain field defs. If not all data from the response are supposed to be be transferred to the dataset, fields can be pre-defined. In this case, the parser skips the search for fields and simply transfers the data if the key name of the JSON object and the field name are matching. Predefining the fields can speed up the process, as the search for fields is not necessary.

Defining a Root Object

Some services might form a response that covers the data into an envelope containing also some meta-information.

You can give the dataset a hint where to find the data of our interest: the root-object. Its value is a string, containing the name or the names of the JSON-entities that contain the data for the adapter. If you want to or have to specify multiple names, concatenate them with dots to form a path.

Usage

Please see an example for this authenticator in the Examples: Google Tasks, Facebook.

Examples

The examples are combined in a single project - the RESTDemos, and the example code is located in your Delphi product samples directory. The main form contains several tab sheets, and each of them represents one example of the REST library. In this tutorial, all examples demonstrate the usage of the components on source level. The same results can - of course - be produced by placing the components on a form and connecting them in a proper way.

Accessing a Simple API

For a first simple example, use the Songsterr service (www.songsterr.com), an online music database with information about artists and their songs. Basic queries can be made without any pre-requisites like registering or authorization.

Create a new project and drop three components onto the form:

  • TRESTRequest
  • TRESTClient
  • TRESTResponse

If you start with the RESTClient first, and then the RESTRequest, and the RESTResponse last, you can see how the components automatically connect to each other.

The API of SongsTerr is described on their website: http://www.songsterr.com/. All types of requests are documented and explained.

For the RESTClient component, you have to set the BaseURL property, which is the main entrypoint for accessing an API. In this case, the BaseURL is set to http://www.songsterr.com/a/ra/. All requests to this service should use that URL as a base. No further configuration is needed for the client component.

Next you set the properties of the RESTRequest component. The Resource property of this component contains the actual query-string. In this example, we want to query basic information about songs by a pattern, so according to the documentation our query-string could be like 'songs.json' (it is also possible to use 'songs.xml' and 'songs.plist' to get responses in the xml and plist formats, respectively). To enter a pattern, you should add a pattern parameter to TRESTRequest. For <pattern>, you can insert something like Madonna, or whatever you like.

That’s it. You can now execute the request in the IDE by right-clicking the RESTRequest component and selecting Execute from the context menu. After a moment, you can see that the property 'content' of the RESTResponse has been updated with the data from the service. You can use LiveBindings to connect that property to a TMemo or a TEdit component.

One side-aspect: You do have to make a 'GET-request', and the pattern is part of the URL. According to the HTTP standards, this value has to be 'url-encoded' if it contains spaces or other 'special' characters. You can do this on your own, or just use a request-parameter of type 'URL-segment'.

Now go back to the RESTRequest parameters and change the pattern parameter to 'Rolling Stones'. Now execute the request again, and you should see that you do not have to care about the space in the band’s name. The space is encoded automatically.

For more information and a sample project that illustrates how to access a simple API service, see Tutorial: Using the REST Client Library to Access REST-based Web Services.

Accessing Twitter API

The Twitter example demonstrates how to connect to the Twitter API using OAuth1 as the authentication mechanism. The OAuth1 workflow is also described in the following link: https://dev.twitter.com/docs/auth/oauth.

The authentication process consists of two steps: first, you have to request an authentication code. This step requires action of the user by entering the user's password. The second step is for requesting the access token, and this is done transparently to the user.

Prerequisite: Registering an Application

Before you can access the Twitter API using your own client, you have to register that client as an application at the Twitter 'developer-console'. This can be done using the following page: https://dev.twitter.com/apps.

After registering the application, you get a 'consumer-key' and a 'consumer-secret' that you will have to use later for authentication.


Step #1: Get a request-token and an auth-code

This is the first part of the OAuth1-workflow. You send your consumer-key and -secret to Twitter’s request-token-endpoint. As an immediate response, you get the 'request-token' and the 'request-token-secret'. Both values are needed later. Now send the request-token back to Twitter, but this time use the access-token-endpoint. You must do this using a web-view, because this is the part where user-interaction is required. (Side-note: Twitter also offers a method called “x-auth” without the requirement of a web-view, but this is only for authorized applications such as the official Twitter-client.)

In the web-view, Twitter displays a six-digit verification-code. This view is very restrictive: The code cannot be selected and copied into the clipboard - you have to transfer the numbers manually.

Step #2: Get an access-token

Using the verification-code from step #1, make a request to the access-token-endpoint and in return you get an access-token and an access-token-secret. Now the request-token and its secret from step #1 are invalid and its values should be cleared - they simply cannot be re-used.

Step #3: Sending a status-update ('Tweet')

Now the OAuth1-Authenticator has all the data it needs to sign a request to the Twitter-API, and this allows you to send your first status-update ('Tweet').

Accessing Google Tasks API

The Google example demonstrates how to access the Google API using OAuth2 as the authentication mechanism. The OAuth2 workflow is also described in the following link: https://developers.google.com/accounts/docs/OAuth2InstalledApp?hl=de.

The authentication process consists of two steps: first, you have to request an authentication code. This step requires action of the user by entering his password. The second step is for requesting the access-token and is done transparently to the user.


Prerequisite: Registering an application

Before you can access the Google API using your own client, you have to register that client as an application at the google-code 'API-console'. This can be done using the following page: https://code.google.com/apis/console?hl=en#access.

While registering the app, you have to choose the apis you want to access. in this example, you simply select and activate 'Tasks API'. Keep in mind that you cannot later access an api that has not been activated for your application. After registering the application, you get a 'client-id' and a 'client-secret' that you have to use later for authentication.


Step #1: Request an authentication-code

The first step of this workflow is to get an authentication code. You do this by opening a webview to a special google url. This url needs the following parameters:

  • response_type=code
    For native apps, google requires this parameter to have a value of “code”.
  • client_id={CLIENTID}
    Here we need to include our own client ID that we have received after registering our application.
  • redirect_uri=urn:ietf:wg:oauth:2.0:oob
    Here you use the constant URI provided by Google because you are using a native client and there is - in contrast to Web applications - no landing page where the user could be redirected.
  • [email protected] (optional)
    To make the workflow easier for the user, Google allows you to provide a login hint. This could be the user's email-address that can be automatically inserted into the login-field so that the user just has to enter his or her password.

Your full url for this example is therefore constructed like this:

 LURL := ' https://accounts.google.com/o/oauth2/auth ';
 + '?response_type=' + URIEncode('code');
 + '&client_id=' + URIEncode( 'INSERT_YOUR_OWN_CLIENT_ID_HERE' );
 + '&redirect_uri=' + URIEncode('urn:ietf:wg:oauth:2.0:oob');
 + '&scope=' + URIEncode(' https://www.googleapis.com/auth/tasks ');
 + '&login_hint=' + URIEncode('[email protected]'); // optional

Step #2: Request an access-token

For this step, you need a TRESTClient and a TRESTRequest:

  LClient := TRESTClient.Create(' https://accounts.google.com/ ');

Next, you create and configure the request object. It contains the following parameters required by google:

  • code={AUTHCODE_FROM_STEP#1}
  • client_id={CLIENTID}
    Client ID for your application, as found in the api console.
  • client_secret={CLIENTSECRET}
    Client secret for your application, as found in the api console.
  • redirect_uri=urn:ietf:wg:oauth:2.0:oob
    Here we use the constant URIprovided by google because we’re using a native client and there is - in contrast to web-applications - no landing page where the user could be redirected to.
  • grant_type=authorization_code
    As defined in the OAuth 2.0 specification, this field must contain a value of “authorization_code”.

In code, the access request should look like this:

  LRequest := TRESTRequest.Create(nil);
  LRequest.Method := TRESTRequestMethod.rmPOST;
  LRequest.Resource := 'o/oauth2/token';
  // required parameters
  LRequest.AddParameter('code', 'AUTHCODE_FROM_STEP#1', TRESTRequestParameterKind.pkGETorPOST);
  LRequest.AddParameter('client_id', 'YOUR_CLIENTID', TRESTRequestParameterKind.pkGETorPOST);
  LRequest.AddParameter('client_secret', 'YOUR_CLIENT_SECRET', TRESTRequestParameterKind.pkGETorPOST);
  LRequest.AddParameter('redirect_uri', 'urn:ietf:wg:oauth:2.0:oob', TRESTRequestParameterKind.pkGETorPOST);
  LRequest.AddParameter('grant_type', 'authorization_code', TRESTRequestParameterKind.pkGETorPOST);

Having the request object prepared, you can just execute it using the client:

  LClient.Execute(LRequest);

After successfully executing the request, you get an access token delivered in the response content. You can just extract this value by querying the response object:

  LClient.Response.TryGetSimpleValue('access_token', LToken);

You have to save the value of the access token because it is needed for every subsequent request to the Google API.

Accessing Facebook API

The Facebook example shows how to access the Graph API of Facebook using OAuth2. The very first step is to get your hands on the 'access-token'. This access token is later your key to open the door to the graph API. Later, when querying the API, you will just use this access token for authentication.

Prerequisite: Registering an Application

Before you can access the Facebook API, using your own client, you have to register that client as an application on Facebook. This can be done using the following page: https://developers.facebook.com/apps.

After registering the application, you get an 'app ID', which you use later for authentication. Please note that this 'app ID' is sometimes referred to as the 'client ID'.


Step #1: Request an Access-Token

Facebook provides a couple of ways for requesting an access token. Because you do not use one of the provided facebook sdks, you should choose the 'Login Flow for Web (without JavaScript SDK)' method. The documentation can be found at the following website: https://developers.facebook.com/docs/facebook-login/login-flow-for-web-no-jssdk/.

You request your access token by opening a Web view to a Facebook url. The base url is https://www.facebook.com/dialog/oauth. You need to add several parameters, but only the first two of them are required. However, the other two are very helpful:

  • client_id={CLIENTID}
    Here you need to include your own client ID that you received after registering your application. It is listed as the 'App-ID'.
  • response_type=token
    Facebook requires that native applications use 'token' as value for 'response type'. This forces the login mechanism to deliver the access token as an additional part of the redirect uri.

Our full url for this example is therefore constructed like this:

  LURL := 'https://www.facebook.com/dialog/oauth'
   + '?client_id=' + URIEncode( 'INSERT_YOUR_OWN_CLIENT_ID_HERE' )
   + '&response_type=token'
   + '&scope=' + URIEncode('user_about_me,user_birthday')
   + '&redirect_uri=' + URIEncode(' https://www.facebook.com/connect/login_success.html ');

The access token is valid for about two hours. It can be refreshed in the background without requiring the user to authenticate again. For further information, please see https://developers.facebook.com/docs/facebook-login/access-tokens/#extending.

Step #2: Fetch user information

You now need the following components: TRESTCient, TRESTRequest, TRESTResponse, and a TOAuth2Authenticator.

Again, the first step is to create the client and the authenticator and to connect them:

 LClient := TRESTClient.Create(' https://graph.facebook.com/ ');

 LOAuth2 := TOAuth2Authenticator.Create(self);
 with (LOAuth2 AS TOAuth2Authenticator) do
 begin
   AccessToken := 'YOUR_OWN_ACCESS-TOKEN_FROM_STEP#1';
 end;
 LClient.Authenticator := LOAuth2;

Next, you have to prepare the request. The resource URI was generated by using Facebook’s Graph Explorer. This URI returns your own name, your own birthday (in case you cannot remember...) as well as the names of your first ten contacts ('friends'). You can find the Graph Explorer at the following website: https://developers.facebook.com/tools/explorer.

 LRequest := TRESTRequest.Create(nil);
 LRequest.Method := TRESTRequestMethod.rmGET;
 LRequest.Resource := 'me?fields=name,birthday,friends.limit(10).fields(name)';

That’s all. You can now use the client to execute the request:

  LClient.Execute(LRequest);

The JSON encoded content can now be found in the response object of the client. Please note that this response object was created on the fly by the client because you did not provide a response object.

  memo_ResponseData.Lines.Text := LClient.Response.Content;

Transforming a Response into a DataSet

A JSON formed response of a service can be transformed into any TDataSet-descendant class. This, of course, requires the data to be in a form that can be transferred into a dataset. This could be either a JSON object or an array of JSON objects. Each object is a new record in the dataset. The properties of the JSON object(s) typically form the columns of the dataset.

See Also