Calling a REST service from JScript

In this post we will take a look how to consume a REST service from the Smart Office JScript code. Related blog posts that focus on M3 APIs can be found at the following links.
Calling M3 APIs in JScript
Background workers in Smart Office scripts

Setting up the REST Data Service

Let’s start with the script code. It’s necessary to import the following three assemblies to gain access to the necessary classes and interfaces.

import MForms;
import Mango.Core.Services;
import System.ComponentModel;

The first step is to get a handle to the REST data service from the DataServiceManager.

var svc : IDataService = DataServiceManager.Current.Get("REST");

The REST data service is configured with a parameter set put in a DataItemCollection, so we start by setting the service address. I’m going to use a fake online REST service for the example to demonstrate how it’s done.

var params = new DataItemCollection();
params.Add(new DataItem(RestDataService.KeyBaseAddress, "https://jsonplaceholder.typicode.com"));

The available parameters can be found in the RestDataService class, the name starting with Key. It’s necessary to set KeyBaseAddress for the REST requests. KeyUri can contain a relative URI that is combined with KeyBaseAddress. Both parameters support variable substitution using names within curly braces {}. For example:

params.Add(new DataItem(RestDataService.KeyUri, "/users/{userId}"));
params.Add(new DataItem("userId", e.Argument));

or like this. Both will will result in the same endpoint URL.

params.Add(new DataItem(RestDataService.KeyUri, "/users/" + e.Argument));

If the userId variable equals to 7 the combined parameters results in the REST URL:

https://jsonplaceholder.typicode.com/users/7

The service request and response type headers must be specified unless the default values are required. The default values are the following two lines, that can be added for clarity but also omitted.

params.Add(new DataItem(RestDataService.KeyAcceptHeader, RestDataService.ContentTypeApplicationXml));
params.Add(new DataItem(RestDataService.KeyOutputType, RestDataService.OutputTypeDataItem));

 

Use the Background Worker to Prevent UI Freeze

Now we’re almost done, but the data service require that it’s running on a background thread. If not, it will throw an Exception to make you aware of that. This is done to prevent potential UI freeze and annoyed users. Let’s have a look at a simple solution by using the BackgroundWorker class.

var worker = new BackgroundWorker();
// Set the function that performs the service call
worker.add_DoWork(OnDoWork); 

// Set the function that performs update and clean-up after the call is done        
worker.add_RunWorkerCompleted(OnRunWorkerCompleted); 

// Launch the service call and provide start parameters
worker.RunWorkerAsync(serviceParameters);

The OnDoWork function will execute on the background  thread and call the data service. When done the OnRunWorkerCompleted function will execute on the UI thread to handle the response and do clean up after the call. Please, always handle Exceptions in your code!

public function OnDoWork(sender : Object, e : DoWorkEventArgs) 
{
    try 
    {
       var serviceParameters = e.Argument;
       // The data service call is done here (this is the background thread)
       e.Result = serverResponse
    } 
    catch(ex) 
    {
       // Exceptions must be handled
    }         
}   
public function OnRunWorkerCompleted(sender : Object, e : RunWorkerCompletedEventArgs) 
{
    try 
    {
       var worker = sender;
       worker.remove_DoWork(OnDoWork);
       worker.remove_RunWorkerCompleted(OnRunWorkerCompleted);
       var serviceResponse = e.Result;
       // The worker has completed its work (this is the UI thread) 
    } 
    catch(ex) 
    {
        // Exceptions must be handled 
    } 
}

The Full Script Example

Here is the full script example for you to test in the ISO Script Tool mforms://jscript:

import MForms;
import Mango.Core.Services;
import System.ComponentModel;

package MForms.JScript 
{
   class RESTDemo 
   {
      var debug;
      var controller;     
      public function Init(element: Object, args: Object, controller : Object, debug : Object) 
      {
         this.debug = debug;
         this.controller = controller;        
         debug.WriteLine("Script Initializing.");            
         DoRestCallOnBackgroundWorker();        
      }
    
      public function DoRestCallOnBackgroundWorker()
      { 
         debug.WriteLine("The REST service call is made on a background worker");          
         var worker = new BackgroundWorker();
         worker.add_DoWork(OnDoWork);
         worker.add_RunWorkerCompleted(OnRunWorkerCompleted);
        
         var userId = 7;         
         worker.RunWorkerAsync(userId);
      }
      public function OnDoWork(sender : Object, e : DoWorkEventArgs) 
      {
         try 
         {
            debug.WriteLine("OnDoWork started");
            debug.WriteLine("Calling a REST service getting DataItem response data");

            var svc : IDataService = DataServiceManager.Current.Get("REST");
            var params = new DataItemCollection();
            params.Add(new DataItem(RestDataService.KeyBaseAddress, "https://jsonplaceholder.typicode.com/"));             
            params.Add(new DataItem(RestDataService.KeyUri, "users/{userId}"));                          
            params.Add(new DataItem(RestDataService.KeyAcceptHeader, RestDataService.ContentTypeTextXml)); // default value                                  
            params.Add(new DataItem(RestDataService.KeyOutputType, RestDataService.OutputTypeDataItem));   // default value
            params.Add(new DataItem("userId", e.Argument)); 
            
            var dataRequest = new DataRequest(params);
            dataRequest.OperationType = "GET";
            var dataResponse = svc.Execute(dataRequest.OperationType, dataRequest);
            e.Result = dataResponse.Data;            
            debug.WriteLine("OnDoWork done");    
         } 
         catch(ex) 
         {
           debug.WriteLine("Exception: " + ex.Message);
           e.Result = null;
         }         
      }
      public function OnRunWorkerCompleted(sender : Object, e : RunWorkerCompletedEventArgs) 
      {
         try 
         {
            debug.WriteLine("OnRunWorkerCompleted running on UI thread");    
            var worker = sender; 
            worker.remove_DoWork(OnDoWork);
            worker.remove_RunWorkerCompleted(OnRunWorkerCompleted);
            if(e.Error != null) 
            {
               debug.WriteLine("Error: " + e.Error.Message); 
               return;
            }
            debug.WriteLine("Fetched user data for:");           
            var dataItem = e.Result;
            debug.WriteLine(dataItem["name"] + " (" + dataItem["username"] + ")" + " Phone " + dataItem["phone"]);  
         } 
         catch(ex) 
         {
            debug.WriteLine("Exception: " + ex.Message);
         }
      }
   }
}

 

Debug output when running the sample in the Script Tool.

Script Initializing.
The REST service call is made on a background worker
OnDoWork started
Calling a REST service getting DataItem response data
OnDoWork done
OnRunWorkerCompleted running on UI thread
Fetched user data for:
Kurtis Weissnat (Elwyn.Skiles) Phone 210.067.6132

4 thoughts on “Calling a REST service from JScript

  1. Stefan

    Great example, but how would the code be, if you would have to send also username and password – authentification basic?? Thx.

    Reply
  2. Stefan

    Hi,
    thx for answer. One question left: I have tried the above script, but I always get following output:

    Script Initializing.
    The REST service call is made on a background worker
    OnDoWork started
    Calling a REST service getting DataItem response data
    OnDoWork done
    OnRunWorkerCompleted running on UI thread
    Fetched user data for:
    Exception: undefined

    Seems that the REST url is called, but the response is empty. Do you have an idea what could be wrong?

    Reply
  3. Peter Grew Post author

    Hi Stefan,
    I tested the sample in this post and got the expected response. I can recommend a debug tool e g Fiddler to examine the request/response to/from the server to see exactly what happens during the call. What version of Smart Office are you running? (see lower right corner of the Canvas)

    Reply

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.