Thursday, January 4, 2018

Delphi XE REST Client Library



http_iconAs I mentioned earlier , among other innovations in Delphi XE5 there was support for authorization by the protocols OAuth 1.0 , OAuth 2.0 , and with this possibility a new library called REST Client Library . Since most of my work needs to write components and modules for working with various APIs - from Google and Facebook to Live Connect and Dropbox API, then, naturally, this new library could not remain without my attention.
And I decided to test the convenience of working with the new library on the services with which I worked most recently, namely with one of the Google APIs - Drive API . Running a little forward, I will say that the library components can also be used when developing mobile applications, but everything in order.

Components of the REST Client Library

So, the whole work of the library is built on the use of three main components:
  1. Customer ( TRESTClient )
  2. Request ( TRESTRequest )
  3. Answer ( TRESTResponse )
Since most web services require prior authorization of a user before providing their API resources to use, the REST Client Library also has four components for authorization and user authentication:
  1. TSimpleAuthenticator
  2. THTTBasicAuthenticator
  3. TOAuth1Authenticator
  4. TOAuth2Authenticator



And, finally, as an additional component, you can select the TRESTResponseDataSetAdapter component, which, in some cases, can be used to convert JSON data to a TDataSet data set.
It should be noted at once that the main data representation format for the REST Client Library is JSON.
Consider the work of some library components with the Google Drive API.

Authorization in the Google Drive API. Testing the connection.

To get started with the Drive API, we need to create a new project in the Google Console and get the Client ID and Client Secret values. On how to do this, I told, for example, in the article " Testing queries to the Google API by means of Delphi. The OAuthClient component for Delphi XE is XE3 . " We will assume that this work was successfully completed and you have the necessary data to start working with the service, and at the same time with the components of the REST Client Library.
Now create a new Delphi XE5 project and drop the TRESTClient, TRESTRequest, TRESTResponse, and TOAuth2Authenticatorcomponents on the main form.
RESTLib_1
Now let's set the necessary parameters, and at the same time test the connection to the Google Drive API.

Obtaining API access keys in Design-Time

We select the component OAuth2Authenticator1 on the form and click on the " Configure ... " link in the Object Inspector:
RESTLib_2
In the window that opens, you must specify the following values ​​for the following fields:
  • Authorization-Endpoint : https://accounts.google.com/o/oauth2/auth
  • Token-Endpoint : https://accounts.google.com/o/oauth2/token
  • Redirctioon-Endpoint : urn: ietf: wg: oauth: 2.0: oob
  • Client-ID : Client ID of your project
  • Client-Secret : the meaning of the Client Secret of your project
  • Access-Scope : https://www.googleapis.com/auth/drive
As a result, the settings window for the component will take the following form:
RESTLib_3
Now you can test the connection. Click the Authorize button and if all the data is set correctly, a Google window will open with confirmation of access to the API resources:
RESTLib_5
and after confirming access in the settings window, the fields of the group " Codes & Tokens " will be filled:
RESTLib_4
Now you can press " Apply " after which all the given values ​​will be transferred to the properties of the component (including the received values ​​from Codes & Tokens).
In principle, even now we can proceed to work with the resources of the API, tk. We already have the necessary access keys. However, when working with the library, we are most likely to need to obtain the access keys in run-time and therefore, in this part of the article it is worthwhile to consider obtaining the access keys without using the above-discussed helper.

Getting access keys in Run-Time

To obtain access keys in run-time, we need:
  1. Call up a window in which the user confirms the right of access to resources
  2. Get the value code
  3. Exchange code for Acess Token
In principle, these steps I have already considered a bunch of times in the blog. Now we will reproduce them using the capabilities of the REST Client Library.
We connect in the uses of the main module of our application module REST.Authenticator.OAuth.WebForm.Win.   This module contains the form Tfrm_OAuthWebForm , which you can use to authorize the user. Tfrm_OAuthWebForm contains TWebBrowser and the Close button and looks like this in the designer:
RESTLib_6
The form defines the following new events:
property OnAfterRedirect: TOAuth2WebFormRedirectEvent read FOnAfterRedirect write FOnAfterRedirect; property OnBeforeRedirect: TOAuth2WebFormRedirectEvent read FOnBeforeRedirect write FOnBeforeRedirect; property OnTitleChanged: TOAuth2WebFormTitleChangedEvent read FOnBrowserTitleChanged write FOnBrowserTitleChanged; type TOAuth2WebFormRedirectEvent = procedure (const AURL: string; var DoCloseWebView: boolean) of object; TOAuth2WebFormTitleChangedEvent = procedure (const ATitle: string; var DoCloseWebView: boolean) of object;
OnBeforeRedirect and OnAfterRedirect work before and after we are redirected to another URL in the browser. OnTitleChanged triggers when the browser's Title property changes.
According to Google's OAuth 2.0 documentation. , after the user gives permission to access their data, Google will return us the value of AuthCode, which we must exchange for the access key. The value of AuthCode is also written out in Title as a string of the type " Success code = ... .. ".
Therefore, obtaining the access key to the Google Drive API in run-time can look like this:
var wf: Tfrm_OAuthWebForm; begin // create a window with a browser to redirect the user to a Google page wf: = Tfrm_OAuthWebForm.Create (self); try // define the event handler for the change Title wf.OnTitleChanged: = TitleChanged; // show the window and open the URL in the browser with an access confirmation form wf.ShowModalWithURL (OAuth2Authenticator1.AuthorizationRequestURI); finally wf.Release; end; // change AuthCode to AccessToken OAuth2Authenticator1.ChangeAuthCodeToAccesToken; // print the key values ​​in Memo Memo1.Lines.Add (OAuth2Authenticator1.AccessToken); Memo1.Lines.Add (OAuth2Authenticator1.RefreshToken); Memo1.Lines.Add (DateTimeToStr (OAuth2Authenticator1.AccessTokenExpiry)); end;
The event handler for the OnTitleChanged event on the window will look like this:
 procedure TForm21.TitleChanged (const ATitle: string;
   var DoCloseWebView: boolean);
 begin
   if (StartsText ('Success code', ATitle)) then
   begin
     OAuth2Authenticator1.AuthCode: = Copy (ATitle, 14, Length (ATitle));
     if (OAuth2Authenticator1.AuthCode & lt; & gt; '') then
       DoCloseWebView: = TRUE;
   end;
 end; 
In this way, we get the key values ​​from Google and can start working directly with the Google Drive API .

Work with TRESTRequest and TRESTResponse. We receive data from Google Drive.

Now you can start getting the necessary data from Google Drive. First of all, we check the settings of the REST Client Library components, which we placed on the form of our application. So,
the TRESTClient component must have the following values:
  • Autenticator = OAuth2Authenticator1
  • BaseURL = 'https://www.googleapis.com/drive/v2'
The BaseURL value is usually specified in the documentation for a particular API and represents that portion of the URL for the request to the server that never changes. In our case, any query to Google Drive will start with a substring https://www.googleapis.com/drive/v2
the TRESTRequest component must have the following values:
  • Client = RESTClient1
  • Response = RESTResponse1
the TRESTResponse component leaves all properties with default values.
Now try to get a list of all the files that are in Google Drive. To do this, I added the TListBox components to output the file names and TMemoto the main application form to display information about a particular file. The main form of the application looks like this:
RESTLib_7
To get a list of files from Google Drive, we need to send a GET request to the URL https://www.googleapis.com/drive/v2/files.
To do this, we use the TRESTRequest component. We write the OnClick handler for the "Get file list" button:
procedure TForm21.Button2Click (Sender: TObject); begin RESTRequest1.Method: = rmGET; // determine the HTTP method - GET RESTRequest1.Resource: = '/ files'; // path to the API resource RESTRequest1.Execute; // execute the query end;
It is worth mentioning here that
the TRestClient.BaseURL properties together with the TRESTRequest.Resource determine the URL to which the request is to be executed.
After the request is executed, the TRESTRequest component will trigger an event:
property OnAfterExecute: TCustomRESTRequestNotifyEvent read FOnAfterExecute write FOnAfterExecute; type TCustomRESTRequestNotifyEvent = procedure (Sender: TCustomRESTRequest) of object;
In this case, the response ( TRESTResponse ) will contain the data received from the server. Write the handler for this event:
procedure TForm21.RESTRequest1AfterExecute (Sender: TCustomRESTRequest); const // server response type cResponseKind: array [0..3] of string = ('drive # fileList', 'drive # file', 'drive # about', 'drive # revision'); var JSONObject: TJSONObject; Kind: string; begin if Assigned (Sender.Response.JSONValue) then begin JSONObject: = Sender.Response.JSONValue as TJSONObject; // learn the server response type Sender.Response.GetSimpleValue ('kind', Kind); case AnsiIndexStr (Kind, cResponseKind) of 0: ParseFileList (JSONObject); 1: ParseFile (JSONObject); end; end; end;
Here, the ParseFileList and ParseFile methods are used to parse JSON and look like this:
procedure TForm21.ParseFile (AJSONObject: TJSONObject); begin Memo1.Lines.Clear; Memo1.Lines.Add ('Title:' + AJSONObject.Get ('title'). JsonValue.Value); Memo1.Lines.Add ('Mime-Type:' + AJSONObject.Get ('mimeType'). JsonValue.Value); Memo1.Lines.Add ('Created Date:' + AJSONObject.Get ('createdDate'). JsonValue.Value); end; procedure TForm21.ParseFileList (AJSONObject: TJSONObject); var FileObject: TJSONObject; Pair: TJSONPair; NextToken: string; ListItems: TJSONArray; I: Integer; begin // get the URL parameter to get the next part of the file list Pair: = AJSONObject.Get ('nextPageToken'); if Assigned (Pair) then NextToken: = Pair.JsonValue.Value; // get the list of files ListItems: = AJSONObject.Get ('items'). JsonValue as TJSONArray; // array elements of the array for I: = 0 to ListItems.Size-1 do begin FileObject: = ListItems.Get (i) as TJSONObject; // get the name of the file ListBox1.Items.Add (FileObject.Get ('title'). JsonValue.Value + '//' + FileObject.Get ('id'). JsonValue.Value); // .... end; // if not all the files are received, then repeat the request, including the pageToken parameter in the URL if Length (NextToken) & gt; 0 then begin RESTRequest1.Params.Clear; RESTRequest1.Params.AddItem ('pageToken', NextToken, pkGETorPOST); RESTRequest1.Execute; end; end;
Note the last part in the ParseFileList method:
if Length (NextToken) & gt; 0 then begin RESTRequest1.Params.Clear; RESTRequest1.Params.AddItem ('pageToken', NextToken, pkGETorPOST); RESTRequest1.Execute; end;
Here, our request component also receives an additional pageToken parameter. This parameter is inserted into the URL when the query is executed, or, if we execute the POST request, into the request body. That is, in our case, the request URL looked like this:
https://www.googleapis.com/drive/v2/files?pageToken=Dfkdf5g67
The collection of Params from TRESTRequest can contain the following types of parameters:
  • pkCOOKIE - the parameter is passed as Cookies
  • pkGETorPOST - parameter is passed as parameter in URL for GET, POST, and PUT requests
  • pkURLSEGMENT - the parameter is inserted into the URL as part of this URL
  • pkHTTPHEADER - the parameter is passed as the request header
  • pkREQUESTBODY - the parameter is passed in the body of the request and, if several parameters of this type are defined, the query will be multi-part.
Let's see how you can use different types of parameters in the query. For example, I did not intentionally parse the entire answer in the OnAfterExecute handler and pull out all the properties of the object out of the answer, of which there is enough , but only the header and identifier. All information about the file will be received by a separate request, which should look like the following in accordance with Google Drive documentation :
https://www.googleapis.com/drive/v2/files/ fileId}
where fileId is the identifier of the object, which we can get together with the header from the previous query. That is, we had the opportunity to use the values ​​from the collection of Params as URL elements. Let's see how this can be done in practice. We will display the information on the file in Memo after clicking on its name in the ListBox. We write such OnClick handler from the ListBox:
procedure TForm21.ListBox1Click (Sender: TObject); var Id: string; begin Id: = Copy (ListBox1.Items [ListBox1.ItemIndex], pos ('//', ListBox1.Items [ListBox1.ItemIndex]) + 2, Length (ListBox1.Items [ListBox1.ItemIndex])); RESTRequest1.Resource: = '/ files / {fileId}'; RESTRequest1.Method: = rmGET; RESTRequest1.Params.AddItem ('fileId', Id, TRESTRequestParameterKind.pkURLSEGMENT); {or so RESTRequest1.Params.AddUrlSegment ('fileId', Id); } RESTRequest1.Execute; end;
Notice how I defined the Resource property-the part in which our parameter should be inserted is surrounded by curly braces, and the name of the parameter is written inside the brackets.
Here, perhaps, is a small example of interaction with Google Drive using the new Delphi XE5 REST Client Library . Now, when each request is executed, OnAfterExecute will be triggered in the handler of which we select the necessary method for parsing JSON: ParseFileList, ParseFile, etc. depending on what we need to get from the server. It only remains to add that in the folder with the sources of the REST Client Library ( {Path_To_Delphi_XE5} \ source \ data \ rest \ restdebugger \ ) a small project on FireMonkey called RESTDebugger is assembled for you. With this unpretentious program you can debug your work with various online services, and generally, see how to use new components when working with FireMonkey.
Above I presented you with a rather simple example of working with new components, without climbing deep into each component and without considering in detail each property and event. But I think that the example presented above will be enough to evaluate the possibilities of the new library.
In general, I say that working with REST Client Library seemed to me quite simple and convenient. There are, of course, what to add to the existing components, but already now the library is quite usable, especially in VCL-projects.

No comments:

Post a Comment