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