Outgoing Web Services - Examples

The Reality HTTP and SOAP API provides the features necessary to enable DataBasic easy access to the Internet and to web services. It can be used to access web pages by simply supplying a URL to a function API and then reading the document. The API supports using a proxy server for gaining access to the Internet. Accessing web services is more complicated as although the API can do the HTTP send/receive part, it is up to the user to generate the SOAP document required to request the service, and to process the response.

The Reality HTTP and SOAP API has been designed to be as flexible as possible, so there may be several different ways to accomplish a given task. In general there is no recommended or best way of using the API, just several alternatives that may be used dependant on context.

The heart of the HTTP and SOAP API is the Request object. This is just a DataBasic variable that holds a dynamic array, the contents of which has various fields to do with requesting a web page or web service operation.

Request objects can be created with RHTTP_INIT_REQUEST(), enabling the request to be modified. Or they can be created on the fly by RHTTP_GET(), RHTTP_POST() or RHTTP_SOAP_REQUEST().

If you need to change or add an HTTP header to your Request, you must use RHTTP_INIT_REQUEST(). You can then use RHTTP_SET_HEADER to set the required header, for example the following code will send the cookie “acct=03847732” with the next use of MyRequest.

MyRequest = RHTTP_INIT_REQUEST(“”,  0, 0)
RHTTP_SET_HEADER(MyRequest, “Cookie”, “acct=03847732”)

Note

HTTP headers remain in the request until they are cleared by setting them to an empty string.

Requests may be freely copied to other variables while they are not ‘active’. Copying ‘active’ requests can lead to unexpected results.

Requests are made ‘active’ by using one of the three functions: RHTTP_GET(), RHTTP_POST() or RHTTP_SOAP_REQUEST().

For example:

MyRequest = “”
URL = "http://google.co.uk/search"
Err =  RHTTP_GET(MyRequest, URL, "q=http")

As MyRequest was an empty string it is constructed on the fly and is updated with the connection details. RHTTP_GET decodes the URL and sets the system name (google.co.uk) and path (/search) in MyRequest. It then connects to google.co.uk and sends the string “/search?q=http”. MyRequest is now ‘active’.

Note

Most of the HTTP and SOAP API functions return an error code, which is an empty string if successful. It is recommended that this error code is always checked for successful operation.

The API supports up to 10 concurrent active requests. So multiple queries can be kicked off at the same time.

To get the results of a request use RHTTP_RETRIEVE(). This function waits for the web server to respond to the given request and returns the resulting document. For example:

Err = RHTTP_RETRIEVE(MyDocument, MyRequest)

In this case Err is the numeric HTTP result code returned where 200 is OK. MyDocument will contain the HTML page returned from Google. After an RHTTP_RETRIEVE (successful or otherwise) MyRequest is no longer active.

A potential problem with using the HTTP/SOAP API within a company environment is that most companies use a proxy server to isolate the internal LAN from the web. This proxy server may or may not use a password. The configuration of the proxy server is set using the function RHTTP_SET_DEFAULT(). This can be used to set the defaults individually, or can it can use an item in a file.

For example, create an item like:

protocol-log=HTTP-LOG
proxy-server=server name or IP address
proxy-port=80
proxy-user=user id
proxy-password=password
no-translate=1

in a file called, for example, HTTP. To load the defaults the use the code:

Err = RHTTP_SET_DEFAULT("defaults-file", "HTTP")

If a user id and password are not required for the proxy server, then these fields can simply be omitted. See RHTTP_SET_DEFAULT for a description of the other options.

Note

It is often useful to setup a protocol log file during development, but in general this should be turned off in a production environment because the volume of data in it can grow very quickly.

Reading an HTML page

Before the HTML/SOAP API can be used, the account being used for development must be setup using SETUP-ACCOUNT feature REALHTTP. Once done the following code can be tried:

INCLUDE #REALHTTP.USER.DEFS FROM /SYSFILES/REALHTTP,BP
*
PERFORM "CLEAR-FILE HTTP-LOG"
*
* The following call sets the name of the defaults file
* and reads the defaults.
ERR = RHTTP_SET_DEFAULT("defaults-file", "HTTP")
*
* Alternative method for setting defaults
*
*      ERR = RHTTP_SET_DEFAULT(ERR, "proxy-server", "bluecoat1")
*      ERR = RHTTP_SET_DEFAULT(ERR, "proxy-port", 80)
*      ERR = RHTTP_SET_DEFAULT(ERR, "proxy-user", "corenet\kwright")
*      ERR = RHTTP_SET_DEFAULT(ERR, "proxy-password", "cora90")
*      ERR = RHTTP_SET_DEFAULT(ERR, "NO-TRANSLATE", 1)
IF ERR # "" THEN
   CRT "Set default failed: "
   PRINTERR ERR
   STOP
END
URL = "http://google.co.uk/search"
RETRY:
*
* Next 3 lines are the fundamental part of the interface – 
* initiate a GET of the page
* then RETRIEVE the document.
*
ERR =  RHTTP_GET(REQUEST, URL, "q=http")
IF ERR = "" THEN
   HTTPERR = RHTTP_RETRIEVE(DOC, REQUEST)
   CRT "HTTP Error = ":HTTPERR
   *
   * NAF loop to overcome CRT's size limitations.
   DOCLEN = LEN(DOC)
   PRINTLEN = 200000
   PRINTPOS = 1
   LOOP WHILE DOCLEN > 0
      CRT DOC[PRINTPOS, PRINTLEN]:
      PRINTPOS += PRINTLEN
      DOCLEN -= PRINTLEN
   REPEAT
   CRT
   *
   * Handle Google’s tendency to return “moved permanently”
   * by extracting the new URL and repeating the request
   *
   IF HTTPERR = RHTTP_CODE_MOVED_PERMANENTLY THEN
      TAG = 'HREF="'
      POS = INDEX(DOC, TAG, 1) + LEN(TAG)
      URL = DOC[POS,-1]
      POS = INDEX(URL, '"', 1)
      URL = URL[1, POS -1]
      CRT "New URL is ":URL
      GOTO RETRY
   END
*
END ELSE PRINTERR ERR
*
* Example of processing the reply header
*
CRT CHANGE(CHANGE(REQUEST<6>, @SVM, ": "), @VM, CHAR(13):CHAR(10))
REPLY = RHTTP_GET_REPLY_HEADER(VERSION, ERRNUM, MESSAGE, INTERN, REQUEST)
CRT "Version=":VERSION:", ErrNum=":ERRNUM:", Msg=":MESSAGE:", Intern=":INTERN

Accessing a web service

Having tried the code that reads an HTML page from a web server you should now have a better understanding as to how the API works.

Accessing an actual web service can be more involved depending on the complexity of the service being used. Web services are accessed using the SOAP protocol. To request a web service to do something you must send a SOAP request document to the appropriate URL and then receive and decode the SOAP response document. The SOAP request and response documents are in XML format and are described by a WSDL file. A WSDL file is an XML document that describes all of the operations the web service provides and how to use them.

The current version of Reality Outgoing Web Services does not enable processing of a WSDL file. It does however provide functionality to generate XML documents (XML.GENERATE) and to extract data from XML documents (XML.QUERY) using templates. The easiest way to see what a web service provides, and how to use it, is to use the freely available SoapUI utility (from http://www.soapui.org/). This takes apart the WSDL file and displays the request and response documents. These can then be cut and pasted into Reality items and edited to turn them into templates. For complex documents it may be necessary to generate several templates and combine them to generate the request document, or use them in sequence to process the response document.

As an example of using a web service the web site www.w3schools.com provides a demonstration web service that converts between degrees Centigrade and Fahrenheit. This can be seen at www.w3schools.com/xml/tempconvert.asmx?op=CelsiusToFahrenheit.

To use this web service you will need to add two items to the HTTP file, by cutting and pasting, either from the web site or from this text:

c2f.evelope
<__nis_cont paramDelim="%"/>
<?xml version="1.0" encoding="utf-8"?>
<soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:xsd="http://www.w3.org/2001/XMLSchema"
	xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">
	<soap12:Body>
		<CelsiusToFahrenheit xmlns="https://www.w3schools.com/xml/">
			<Celsius>%1%</Celsius>
		</CelsiusToFahrenheit>
	</soap12:Body>
</soap12:Envelope>

c2f.result
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:xsd="http://www.w3.org/2001/XMLSchema">
	<soap:Body>
		<CelsiusToFahrenheitResponse xmlns="https://www.w3schools.com/xml/">
			<CelsiusToFahrenheitResult>%1%</CelsiusToFahrenheitResult>
		</CelsiusToFahrenheitResponse>
	</soap:Body>
</soap:Envelope>

Note

The string string used on the web site has been replaced by %1% in both templates so that they can be used in XML.GENERATE and XML.QUERY subroutines.

What follows is some DataBasic that uses this web service; you INPUT a temperature in Celsius and the Fahrenheit conversion is displayed via a CRT.

Note

"Action" is a web services term. It is just a name the service recognises. For example, a maths service could have actions of "sine" or "cosine". An action is often a URI, to make it unique across the web, but it doesn't need to be.

INCLUDE #REALHTTP.USER.DEFS FROM /SYSFILES/REALHTTP,BP
 *
 NL = CHAR(13):CHAR(10)
 * Setup any proxy server configuration required.
 Err = RHTTP_SET_DEFAULT("defaults-file", "HTTP")
 IF Err # "" THEN
    CRT "Set default failed: ":
       PRINTERR ERR
    STOP
 END
 * Now read in the two templates we need
 OPEN "HTTP" TO FD ELSE STOP 201
 READ EnvelopeTemplate FROM FD,"c2f.envelope" ELSE
    CRT "Cannot read envelope"
    STOP
 END
 READ ResultTemplate FROM FD,"c2f.result" ELSE
    CRT "Cannot read result template"
    STOP
 END
 *
 EndPoint = "https://www.w3schools.com/xml/tempconvert.asmx?op=CelciusToFahrenheit"
 * In this case Action is taken from the SOAPAction: line in the HTTP header
 * in the example
 Action = "https://www.w3schools.com/xml/CelsiusToFahrenheit"
 Request=""
 *
 LOOP
    *
    CRT "Enter temperature in Celsius: ":
    INPUT Temp
    *
    WHILE Temp # "" DO
       *Combine the temperature into the request template
       CALL XML.GENERATE(Err, Envelope, EnvelopeTemplate, Temp)
       IF Err # "" THEN GOTO EXIT
       *Send the request
       Err = RHTTP_SOAP_REQUEST(Request,  EndPoint, Action, Envelope)
       *
       IF Err # "" THEN
          CRT "Request failed:"
          PRINTERR Err
       END
       *Get the response
       Err = RHTTP_RETRIEVE(DOC, Request)
       IF Err = RHTTP_CODE_OK THEN
          *CRT CHANGE(DOC, ">", ">":NL)
          *CRT LEN(DOC)
          *Use the response template to process the result
          CALL XML.QUERY(Err, Result, DOC, ResultTemplate)
          *
          IF Err # "" THEN GOTO EXIT
          CRT Temp:" Celsius is ":Result:" Fahrenheit"
          *
       END ELSE
          CRT "Error = ":Err
          CRT CHANGE(DOC, ">", ">":NL)
          STOP
       END
    REPEAT
    STOP
 EXIT:
       CRT "XML parser error: ":Err
    STOP