Web Request API

Overview

Answer Generators implemented in Javascript have access to the Web Request API which allows them to make web requests, originating on the server side, to public web servers. This API is useful for calling external web APIs, especially when secret information, such as API keys, should be transmitted with requests. This page describes the Web Request API in detail.

There are two ways to make web requests: the jQuery-like API and the fluent API.

jQuery-like API

The jQuery-like API is inspired by the ajax() method in jQuery. Here is an example of using the jQuery-like API:

function onSuccess(responseText, response) {
  const obj = JSON.parse(responseText);

  // do some processing ...

  return [{
    label: 'Some Stuff',
    uri: 'http://somestuff.com/inventory/' + encodeURIComponent(obj.itemId),
    // ...
  }];
}

function onError(response) {
  return null;
}

function generateResults(q, recognitionResults, context) {
  // do some processing ...

  const url = 'http://somewebco.com/services/info';

  const request = HostAdapter.makeWebRequest(url, {
    method: 'POST',
    contentType: 'application/json',
    accept: 'application/json',
    body: {
      "name" : "toasters",
      "originCountry" : "US"
    }
  });
  request.send('onSuccess', 'onError');
  return HostAdapter.SUSPEND;
}

In the above example, first, a WebRequest object is created by calling HostAdapter.makeWebRequest() with two arguments:

Name Description
url The URL to send the request to. It must point to a public host.
options A Javascript object containing the following key/value pairs:
Key Value
method The request method to use. Supported methods are HEAD, GET, POST, PUT, DELETE, and PATCH.
contentType The content type of the request body. Used to set the Content-Type header of the request, and if set to application/json or application/x-www-form-urlencoded, the body option will be serialized to a string appropriately. If not set and the Content-Type header is not set either, and the body is a Javascript object, application/json will be assumed.
accept Used to set the Accept header of the request.
body The response body, as a string, or if in the case of using a content type of application/json or application/x-www-form-urlencoded, a Javascript object that will be converted to a JSON string or a URL encoded string.
headers A Javascript object that maps request headers to values
processBody If true or not specified, and the Content-Type response header includes /xml, the response body will be treated as XML and stripped of XML processing instructions and comments before passing back to your script. This allows the E4X API to be used with new XML(responseText) (new XML() doesn't handle XML processing instructions or comments properly). XML stripping can be disabled by setting processBody to false.

Next, the web request is actually sent to the remote server by calling request.send(), which is an asynchronous call that returns immediately. The first argument, 'onSuccess' in this example, is evaluated later if the request completes successfully. The second argument, 'onError' in this example, is optional and evaluated later if the request fails. If the second argument is not specified and the web request results in a failure, the Answer Generator is terminated and considered to have not produced any results.

After sending the request, the script returns HostAdapter.SUSPEND which suspends execution of the script until an event occurs. In this case, the script is waiting for the response of the web request.

If the remote server returns a successful response, the execution environment evaluates the first argument of request.send() and treats it as a function. It then calls the function with the following two arguments:

Name Description
responseText The response body, as a string
response A Javascript object containing the following key/value pairs:
Key Value
statusCode The HTTP status code of the response
body The response body, as a string
headers A Javascript object that maps response headers to values

If an error occurs while sending the request, or the remote server returns a unsuccessful response, the execution environment evaluates the second argument of request.send() and treats it as a function. It then calls the function with the response object as in the case of a successful response, if it is available. If the request cannot be sent at all, the response object will be null.

Tip You can pass in some of the available variables into the success or error handler by specifying an expression that evaluates to closure like so:

function makeSuccessHandler(q) {
  return function (responseText, response) {
    // q is now available here ...

  };
}

function generateResults(q, recognitionResults, context) {
  const request = HostAdapter.makeWebRequest(url, {
    // ...
  });
  request.send('makeSuccessHandler(' + JSON.stringify(q) + ')');
  return HostAdapter.SUSPEND;
}

Watch out The callback expressions are natively executed by Rhino, so not all ES6 features are available.

Fluent API

Instead of using the jQuery-like API, you may choose to use the fluent API which allows you to chain method calls to set the options for the request. Here is the example usage of the jQuery-like API converted to use the fluent API instead:

function onSuccess(responseText, response) {
  // same as with the jQuery-like API ...
}

function onError(response) {
  // same as with the jQuery-like API ...
}

function generateResults(q, recognitionResults, context) {
  // do some processing ...

  const url = 'http://somewebco.com/services/info';

  HostAdapter.makeWebRequest().setUrl(url).setMethod('POST').
    setContentType('application/json').setAccept('application/json').setBody({
      "name" : "toasters",
      "originCountry" : "US"
    }).send('onSuccess', 'onError');
  return HostAdapter.SUSPEND;
}

As you can see, the setXyz() methods of WebRequest set the option xyz, then return the request object so methods can be chained. Sending the request, evaluating the callback methods, and suspending the execution are performed in the same way as in the jQuery-like API.

Security Considerations

Users of Answer Generators implemented in Javascript don't have access to the corresponding source code. But for source control, you probably don't want to have secrets (such as API keys) embedded in your code. The best place to store secrets is in your Developer Settings.

Limitations

To protect the security and performance of the answer service, the following limitations to the Web Request API apply:

  • The entire request, including headers and body, must be less than 25 KB
  • The entire response, including headers and body, must be less than 1 MB
  • The remote server must respond within 3 seconds
  • A maximum of 3 redirects are followed
  • No more than 5 web requests may be executed in a single script