Chapter 1—Web Services

The Web Services component provides support for RESTful web services for client and server. The component instantiates a Web Worker Object with properties and methods based on the type of web service you are using. You must create the client interface (remote forms or window classes etc) to the Web Service Object.

In addition, the Web Server plug-in allows the Omnis App Server to expose your Omnis code as a RESTful Web Service. This is described under the Creating your own Web Services section.

In addition, there is a JSON external component, called OJSON, that allows JSON based objects returned from RESTful resources to be manipulated: this is described in the OJSON chapter.

From Studio 8.1 onwards, you no longer need to serialize Omnis with a separate Web Services plug-in serial number in order to develop a Web Services client or to consume a Web Service. If you are creating your own Web Services, from your Omnis code, your server will still require an Omnis App Server deployment license when you are ready to deploy your app.

Note that the old WSDL-based Web Services component available in versions prior to Studio 8.x is no longer supported in Omnis and has been removed.

What is REST?

REST (Representational State Transfer) is the predominant architectural style that is used to consume and publish Web Services, and is seen as an alternative to other distributed-computing specifications such as SOAP. A RESTful Web Service is identified by a URI, and a client interacts with the resource via HTTP requests and responses using a fixed set of HTTP methods.

A RESTful Web Service follows the following rules to provide access to the resources represented by the server:

  1. The resource must be identified by a URI, which is a string of characters similar to a web address, that points to the resource.

  2. A client interacts with the resource via HTTP requests and responses using a fixed set of HTTP methods.

  3. One or more representations of a resource can be returned and are identified by media types.

  4. The content of a resource can link to further resources.

There are two sides to consider for RESTful Web Services:

  1. a Client, which would be an Omnis library containing methods to consume a RESTful Web Service,

  2. and the Server, where you can implement a RESTful Web Service by exposing the business logic (remote task methods) in your Omnis library to be consumed by clients.

Example Library

There is an example library that demonstrates the capability of Omnis to consume a RESTful web service – the library is available with a Tech Note: TNWS0002 which is available on the Omnis website.

The example library presents basic weather forecast information by consuming a web service provided by openweathermap.org. The example is based on the free API service which you can use for the example.

API Key

To use the web service consumed in the example library, you must obtain a trial API key from openweathermap.org (which must not be shared with other people): note that the API key we used to create and run the online demo has been removed from the example library and you will need to obtain your own API key.

When you open the example Omnis library, you will be prompted to enter an API key. This value is stored in the remote task in the tAPIKey task variable, and you should not be asked to enter it again.

If you reuse any portion of the example app for your own development and deployment, or create your own application using the weather data from openweathermap.org, please remember to obtain a paid-for API key for your own or your clients use.

Testing the Example Library

To test the web service and display the weather forecast, open the example library, right click on the jsWeather remote form and select 'Test Form' from the context menu. The form should open in your desktop web browser and show the current weather for Saxmundham (the home town for the Omnis development team in the UK) – the same information can be accessed in a table format by selecting the Table hyperlink. In the main remote form there is a pictorial summary of today’s weather with the maximum & minimum expected temperatures, along with the forecast for the next four days. You can find the forecasts for other locations by entering either the city name or zip/post code.

The World tab gives a summary forecast for 20 selected cities in the world. Since the example library uses the free version of the API, all data is cached within the example library to prevent unnecessary calls back to the server. You can view the example app online on our hosted server.

If you publish the form to a webserver, when the form opens, it will try to identify your location using the IP address returned from the remote task. If this fails, it will revert to ‘Saxmundham’ as the default location.

Queueing RESTful requests & Licensing

RESTful requests to the Omnis Server consume a web user license for the duration of the request. In versions prior to Studio 8.1, if all licensed connections were in use when a new RESTful request came into the server, the client received an error. In Studio 8.1 onwards, RESTful requests are now queued internally until they succeed. Note that requests will never be re-queued in a single threaded server (a server where Start server has not been called) since everything executes sequentially.

In addition, there is a new sys function, sys(234), which returns a row of information containing statistics about RESTful requests to the Omnis server. The row has three columns: column 1 is the count of successful calls; column 2 is count of calls resulting in an error; and column 3 is the count of calls internally re-queued because there was not a free user.

Creating a Web Services Client

There are a few key requirements for creating a Web Services Client which are:

  1. An HTTP client that allows resources to be submitted and received using various HTTP methods.

  2. An HTTP client that allows HTTP headers to be specified for requests, and analysed for responses.

  3. A means to manipulate the important media types for RESTful resources: XML or JSON.

  4. Support for HTTPS, if required, which in a business environment is usually essential.

  5. Support for HTTP basic and digest authentication.

The Web Services Client is implemented as an External Component Object. The External Objects group in Omnis Studio includes a group called OW3 Worker Objects: this contains a HTTPClientWorker object which is an HTTP Worker Object (along with FTP, SMTP and IMAP objects). NOTE to existing users: in versions prior to Studio 8.1 you needed to install and configure Java in order to use the HTTPClientWorker object in the Web Worker Objects group, but the new HTTP worker object in the OW3 Worker Objects group does not require Java.

The HTTP worker object functions in a similar manner to the DAM worker objects, although there is a simplification in the way they handle re-use of the object when a request is currently in progress: see the notes about $init.

To use the HTTP worker object, you need to create an Object Class which is a subclass of HTTPClientWorker. Having created a new Object class, set its $superclass property to the name of the HTTPClientWorker object by clicking on the dropdown list and selecting the HTTPClientWorker object in the OW3 Worker Objects group in the Select Object dialog.

image4

In the object class, the methods $completed and $cancelled are inherited from the superclass (the HTTP worker object) which the client worker calls with either the results of a request, or to say the request was cancelled.

You then need to create an Object instance variable in your JavaScript remote form (or window class) based on the new Object class to instantiate the object and allow you to interact with the web service by running its methods.

image5

Properties

The HTTPClientWorker object has the following properties:

$state

A kWorkerState... constant that indicates the current state of the worker object.

$errorcode

Error code associated with the last action (zero means no error).

$errortext

Error text associated with the last action (empty means no error).

$threadcount

The number of active background threads for all instances of this type of worker object.

$proxyserver

The URI of the proxy server to use for all requests from this object, e.g. http://www.myproxy.com:8080. Must be set before executing $init for this object.

$timeout

The timeout in seconds for requests from this object. Zero means default to the standard timeout for the HTTP client. Must be set before executing $init for this object.

$shareconnections

(Only applies to the old HTTPClientWorker object in the Web Worker Objects group) If true (default) the object shares connections to HTTP servers with other HTTPClientWorker objects rather than managing its own set of connections (consider authentication when setting this - different objects may need different authentication credentials, in which case you should not share connections). Must be set before executing $init for this object.

Methods

The HTTPClientWorker object has the following methods:

$init()

$init(cURI,iMethod,lHeaders,vContent[,iAuthType,cUserName,cPassword,cRealm]) Initializes the worker object so that it is ready to perform the specified HTTP request. Returns true if the worker was successfully initialized. The parameters are:

Parameter Description
cURI The URI of the resource, optionally including the URI scheme (http or https), e.g. http://www.myserver.com/myresource. If you omit the URI scheme, e.g. www.myserver.com/myresource, the URI scheme defaults to http
iMethod A kOW3httpMethod... constant that identifies the HTTP method to perform: kOW3httpMethodDelete, kOW3httpMethodGet, kOW3httpMethodHead, kOW3httpMethodOptions, kOW3httpMethodPatch, kOW3httpMethodPost, kOW3httpMethodPut, kOW3httpMethodTrace
lHeaders A two column list where each row is an HTTP header to add to the HTTP request. Column 1 is the HTTP header name, e.g. 'content-type' and column 2 is the HTTP header value, e.g. 'application/json'
vContent A binary or character variable containing the content to send with the HTTP request. If you supply a character variable, the worker converts it to UTF-8 to send in the request. Content can only be sent with Patch, Post and Put methods
iAuthType A kOW3httpAuthType... constant that specifies the type of authentication required for this request. If you omit this and the remaining parameters, there is no authentication (and this parameter defaults to kOW3httpAuthTypeNone). Supported values are kOW3httpAuthTypeNone, kOW3httpAuthTypeBasic and kOW3httpAuthTypeDigest
cUserName The user name to use with authentication types kOW3httpAuthTypeBasic and kOW3httpAuthTypeDigest
cPassword The password to use with authentication types kOW3httpAuthTypeBasic and kOW3httpAuthTypeDigest
cRealm The realm to use with authentication type kOW3httpAuthTypeDigest

NOTE: If you call $init when a request is already running on a background thread, the object will cancel the running request, and wait for the request to abort before continuing with $init.

$run()

Runs the worker on the main thread. Returns true if the worker executed successfully. The callback $completed will be called with the results of the request.

The following method is from the example library – the method initializes the Web Services object, having already setup the main parameters for the $init() method, and calls the web service.

# iURI (Char) initialized as "http://api.openweathermap.org/data/2.5/weather"
# The full URI sent has the API key, location appended as well as an optional parameter to return the data in metric format
# iHTTPMethod (Int) set to kOW3httpMethodGet
# iHeadersList (List)
# iContentChar (Char)
Do iHeadersList.$define(iHeaderNameiHeaderValue)
Do iHeadersList.$add("content-type""application/json")
# call the web service
Do iRestfulObj.$init(iURIiHTTPMethodiHeadersListiContentChar)
Do iRestfulObj.$run() Returns lStatus

See $completed for handling the response from the web service.

Note: $run should not be used if you are hosting the RESTful service you are calling in the same instance of Omnis as your client. This would prevent the server-side execution from running and hang Omnis.

$start()

Runs the worker on the background thread. Returns true if the worker was successfully started. The callback $completed will be called with the results of the request, or alternatively $cancelled will be called if the request is cancelled.

$cancel()

If required, cancels execution of the worker on the background thread. Will not return until the request has been cancelled.

Method names

If you add further methods to your web services object you should avoid using names that may conflict with any possible reserved words on your system.

Callbacks

The HTTPClientWorker object calls the following callbacks, which must be defined in your object class:

$cancelled

Called to report that the request has been cancelled.

$completed

Called to report completion of the request. It has a single row variable parameter with columns as follows, including the content returned in the final column:

Column Description
errorCode An integer error code indicating if the request was successful. Zero means success, i.e. the HTTP request was issued and response received - you also need to check the httpStatusCode to know if the HTTP request itself worked
errorInfo A text string providing information about the error if any
httpStatusCode A standard HTTP status code that indicates the result received from the HTTP server
httpStatusText The HTTP status text received from the HTTP server
responseHeaders A row containing the headers received in the response from the HTTP server. The header values are stored in columns of the row. The column name is the header name converted to lower case with any - (hyphen) characters removed, so for example the Content-Length header would have the column name contentlength
responseContent A binary column containing the content received from the server

The following is the $completed method in the oRest object in the example library:

# called by the client worker with the results
Calculate iResponse as pRow
Calculate iResponseHeaders as pRow.responseHeaders
Do OJSON.$formatjson(pRow.responseContent) Returns iReturnStr
Do OJSON.$jsontolistorrow(pRow.responseContent) Returns iJSONRow

The JSON external component can be used to process the JSON output.

CA Certificates

To make your RESTful based connection secure, you need to add a certificate file (.crt) to the appropriate place on your operating system. On Linux, the default installation for handling certificates is Open SSL; the CA certs are typically installed in /etc/ssl/certs/ca-certificates.crt.

The client certificates are specified by the last three arguments of the $setsecureoptions() method (which must be called before calling $run or $start).

The last three arguments are:

You can find more information about valid certificate store path expressions on the CURL website (https://curl.haxx.se).

Creating your own Web Services

There is a Web Server plug-in which allows you to expose the code in your Omnis applications, in an Omnis RESTful remote task, and provide them as Web Services for any clients to consume. The interface for the web services you can create and provide to clients is exposed as an API or set of APIs. The key requirements for Omnis to act as a server or provider of RESTful based Web Services are:

These requirements can be met with a combination of the Omnis App Server and a standard HTTP Web Server.

There is a tech note and example library on the Omnis website to show how you can implement a Web Service from your Omnis code: TNWS0003: “Getting Started with RESTful Web Services, Tomcat and Swagger UI”.

To deploy your Omnis-based Web Service, you will need to setup the Omnis App Server, using the Web Services Server plug-in (mod_OmnisREST.so) rather than the standard plug-in, which is detailed in the tech note: TNJS0003: “Setting Up The Omnis App Server”.

A RESTful remote task can have a superclass in another library, provided that the superclass in the other library does not contain URIs. This allows you to use framework libraries.

Omnis RESTful APIs

Omnis RESTful APIs (or ORAs) can be fully defined using a “Swagger” definition, which is the most widely used standard for defining RESTful APIs. This section assumes you are using Swagger, but from Studio 10.2 you can use OpenAPI to define an Omnis RESTful API; see OpenAPI definitions.

Omnis Studio supports Swagger 2.0 for RESTful web services. This only affects the Swagger files Omnis generates, and there is just one definition per service. The Web Service Server library also has a link to save the Swagger file for a service to disk. The reasons for choosing swagger include:

Note however that there is nothing in the current implementation that requires a developer to use the Swagger definition in REST based web services.

Omnis uses the first non-empty description it can find for a remote task in the service as the description of the service in the Swagger file.

Web Services in the Omnis IDE

Omnis RESTful APIs are visible in the Studio Browser as children of the library node beneath the Web Service Server node (including Swagger and OpenAPI definitions). Each ORA is shown a separate node icon in the tree, and various options or actions are shown as hyperlinks when an ORA is selected. Omnis RESTful APIs have a method list and an error log that you can use to manage the service.

Omnis RESTful APIs have Swagger definitions that can be viewed using the IDE browser hyperlinks for the ORA. There is a hyperlink for the top-level resource listing, and a separate hyperlink for each top-level URI component. Clicking on a link displays the relevant Swagger data for the link (building it if necessary first). In the top of the panels is a read-only URL; you can select the text for the URL, and paste it into a browser or into Swagger UI (in the latter case, the resource listing URL is the only URL you would use). Note that you need to be aware of potential CORS issues when using these links in Swagger UI (see the later section on CORS).

Creating an Omnis RESTful API

To create a Web Service or Omnis RESTful API you need to set some properties of a remote task and add some RESTful methods. The remote task class has two properties to allow you to setup the Web Service:

To create an Omnis RESTful API (ORA), set the $restful property of a remote task to kTrue, and provide a name in $restfulapiname (this name will appear in the Studio Browser when you have added some methods). Note: the $restful property is an inherited property, so if you create a subclass of a remote task with $restful set to kTrue, the subclass will also be $restful. Further note a remote task with $restful set to kTrue is not yet a member of an ORA. For each remote task that is to belong to an ORA (meaning that it provides URIs and methods for clients to call) set $restfulapiname. Note that all remote tasks in an ORA must be in the same library. The $restful and $restfulapiname properties are available at runtime in remote task instances.

After setting $restful and $restfulapiname for a remote task class, the new ORA will not appear in the browser, because it has not implemented any RESTful methods. Therefore, the next step is to open the method editor for the remote task, in order to add objects and methods.

When a remote task is RESTful, the remote task has a group of objects (named $objs). These objects are the URIs exposed by the remote task to clients. Inheritance works with these objects and their methods in the same way that it works with other Omnis classes that support inheritance. However, $cfield and $cobj are not resolved for URI objects.

URIs

A URI must have one or more components starting with /. Parameter place-holders can be included as component two or later as {paramName} where paramName is unique (case-insensitive) in the URI. The URI cannot have a trailing / and cannot be duplicated. For example:

In the second case, userId is a parameter place-holder, meaning that the RESTful methods implemented for the URI must all have a parameter named userId which Omnis populates with the userId from the addressed URI.

A URI is considered to be a duplicate (and therefore not allowed in the remote task) if it has the same number of components of another URI in the remote task or one of its superclasses, and all components match; components match if neither is a parameter place-holder and they have the same case-insensitive value, or if either of the components is a parameter place-holder.

HTTP methods

URIs are like other class objects in classes with instances, in that they can have their own methods. There are some special methods supported for URIs, called HTTP methods. These correspond directly to the HTTP protocol methods used by a RESTful API, and they are:

The HTTP methods are named with a leading $ (unlike the HTTP protocol methods) so that they work with the usual Omnis inheritance mechanism. URIs can also have other methods, but these are not HTTP methods and are not part of the public ORA. The name is the only property that determines if a URI method is an HTTP method, so renaming a method can make it become HTTP or non-HTTP accordingly.

Query Parameters

HTTP methods of a URI have some special features and properties. The first parameter for all HTTP methods must be named pHeaders, and defined as a Field reference. This references a row which contains the HTTP headers received in the RESTful request from the server. The row has a column for each HTTP header. The column names are created by converting the HTTP header name to lower case and removing any - characters e.g. Content-type becomes contenttype as a column name. If more than one header exists with the same name, the headers are combined into a single comma-separated value.

For methods which accept content with the request ($patch, $post, $put) the second parameter must be named pContent, which is a Field reference to the content received in the request.

When you create a new HTTP method, Omnis creates the parameters pHeaders and pContent automatically, and it also adds a character parameter for each parameter place-holder in the URI. In addition, you can add further parameters to the method (which must be of type character, Boolean, integer or number). Each further parameter is then expected to be part of the query string in the full URL used to make the RESTful call to the method; if you provide an initial value for the parameter, the parameter is optional in the query string.

When the RESTful call reaches the remote task method, pHeaders, pContent, the place-holder parameters and the query string parameters are all automatically populated by Omnis.

Note that once you have created the method, you can delete parameters which are required at runtime, e.g. pHeaders. However, Omnis will detect this and generate an error, either at the ORA level (see the error log in the browser) or when the client attempts to call the method.

An HTTP method has some additional properties:

The HTTP method properties affect how Omnis interacts with the HTTP method:

Object array output type

When $sendlistsasobjectarray is set to true, the JSON generated by Omnis for a returned row or list that contains lists, contains arrays of objects rather than arrays of arrays (in this case the lists must only contain columns with simple types). There is one exception to this rule. If the list to be converted to JSON has a single column named "<array>", Omnis outputs the list as an array.

There is a checkbox on the RESTful panel for the HTTP method in the method editor, that allows you to select this option.

Note that this option applies to both rows returned by the method, and lists returned by the method when the return type is schema[]. In the latter case, the top-level array returned is always an array of objects, therefore you should note that the new option applies to lists contained in the returned list.

Unknown Query String Parameters

RESTful methods can allow unknown query string parameters. The RESTful panel for a RESTful method in the Method Editor has a checkbox option "Allow unknown query string parameters" (the default is unchecked). When checked, it means the RESTful server will accept requests that contain query string parameters that are not specified in the method parameters. The remote task instance can access these unknown parameters using the notation $cinst.$unknownquerystringparams. The properties are:

Escaping String Parameters

The “Escape query string parameters” option allows you to control whether or not string parameters are URI escaped; it defaults to true (replicating the behavior in versions prior to Studio 11), meaning that query string parameters are URI escaped. When turned off, the query parameters are not URI escaped, allowing you to perform any character encoding conversion yourself. For example, if you receive UTF-8 data instead of ASCII, you could turn this option off and escape the text using the ow3.escapeuritext() function.

Simple Types

In a schema class, a list column can have a so-called simple type as its sub-type. Valid values are <character>, <integer>, <boolean> and <number>. These allow ORAs to define JSON that contains arrays of simple types.

Method Editor

The method editor has additional features for a RESTful remote task. There are menu items that allow you to:

These menu items are on the context menu for the method tree, and also in the modify menu in the toolbar, provided that the method tree has the focus.

In addition, when the currently selected method is an HTTP method, the variables panel has two additional tabs: RESTful and RESTful notes:

Find and replace works with the RESTful properties; double clicking on a RESTful entry in the find and replace log will open the method editor with the property selected.

The Code Assistant lists column names after you enter the name of a list or row variable with a schema or table class as its subtype.

Server Properties and URLs

The Server Configuration dialog allows two properties to be configured:

These properties are stored in the config.json file in the Studio folder of the Omnis tree. These properties affect the URLs stored in the Swagger definitions for ORAs implemented in the server.

If you do not set these properties, then the API will be defined to connect directly to the built-in HTTP server in Omnis.

In order to make a call to an ORA, you need a URI. If you look at an ORA in the IDE browser, you can see how the URIs are constructed by looking at the Swagger definition for a top-level URI path (using one of the hyperlinks immediately below the Resource listing hyperlink). The base path will be something like:

http://localhost:8080/omnisrestservlet/ws/5988/api/phase2/myapi

The initial part of the URL (http://localhost:8080/omnisrestservlet) gets the request as far as the Web Server plugin. The next two components of the URL (/ws/5988) tell the Web Server plugin how to connect to Omnis or the load sharing process. These two components are optional, and can be replaced with the Omnis-server header property described in the Phase 1 documentation; however, if you are likely to be doing cross-domain requests, then it is better to use the /ws/5988 form, since it is guaranteed to be sent with an OPTIONS method request. (Note that the “ws” is a fixed value). The second component (5988) has the general syntax definition:

The remaining components are forwarded to the Omnis server: /api/phase2/myapi. The first of these remaining components is a fixed value, which tells the Omnis server that this is a call to an ORA (this first component can also have the fixed value swagger as part of a URL to request a Swagger definition, or it can be of the form LIB.RT, as used in Phase 1 of the RESTful server implementation). The next two components are the library name and the ORA name.

When connecting directly to the Omnis server, the base URL is something like:

http://localhost:5988/api/phase2/myapi

Finally, when combined with the URI in a remote task in the server, the URL used to call an HTTP method for URI /users/{id} (with no query string parameters) is something like:

http://localhost:8080/omnisrestservlet/ws/5988/api/phase2/myapi/users/1234

ORA Properties and Methods

There are various properties and objects to support ORAs. As described earlier, the remote task has the properties $restful and $restfulapiname. RESTful remote tasks have a $objs group. Specific methods in this group are:

HTTP methods in a RESTful remote task have the following properties:

In addition, HTTP methods have a group:

To add a new response code to the group, use:

The members of the HTTP response codes group have properties as follows:

There are two properties in $root.$prefs (which are also in config.json):

A RESTful remote task $construct method receives a row variable parameter with the following columns:

  1. url or fullurl
    url is the partial url starting with the Omnis library component, or
    fullurl contains the full URL, starting with the path to the script, e.g. /omnisrest/ws/5988/api/...

  2. method
    the name of the HTTP method.

The host name used can be obtained from the host header. There is no way to determine if the request was made using http or https.

Swagger Definitions

Omnis populates the Swagger definitions using the properties of the remote task. The Swagger method summary is the Omnis method description. A schema column with no nulls set to kTrue is marked as a required JSON member in the Swagger model object.

The Swagger resource listing contains various fields that need to be populated e.g. API version number, contact email etc. In order to do this, the Omnis tree contains a default template, and you can also create specific templates for specific ORAs. The default template is the file ‘default.json’ in the folder clientserver/server/restful/swaggertemplates in the Studio tree. You can edit this, or alternatively copy it and create an ORA specific template, which must have the name <restfulapiname>.json, and be stored in a sub-folder of swaggertemplates named with the library name e.g.

clientserver/server/restful/swaggertemplates/lib/myapi.json

Omnis reads the template each time it generates a new resource listing. Omnis keeps the Swagger definitions in step with changes in the environment e.g. when you save a remote task or relevant schema class, or change the RESTful URL or connection property.

You can use swagger-ui (https://github.com/wordnik/swagger-ui) with the built-in web server to test your ORAs. Take the dist folder for swagger-ui, drop it into the webapps folder of your web server tree, and rename it swagger-ui. Restart the web server. You can then use the URL http://localhost:8080/swagger-ui/index.html#!/path in a browser to open swagger-ui.

If you also place omnisrestservlet in web server ‘webapps’ folder (and restart the web server), and set Omnis server properties restfulconnection to your server port, and restful URL to http://localhost:8080/omnisrestservlet, you can use swagger-ui without any cross-domain issues.

If you select your ORA in the Web Service Server node of the IDE browser, you can click on the Resource listing hyperlink, and copy the URL from the top of the panel showing the Swagger definition. Paste the URL into swagger-ui and press Explore - you should see your ORA.

OpenAPI Definitions

OpenAPI is a more up to date version of the RESTful API description format, and Studio 10.2 now generates OpenAPI 3.0.0 definitions, as well as Swagger 2.0 definitions.

When you select a RESTful service beneath the Web Service Server node in the browser, there are now two pairs of links:

The OpenAPI definition can be retrieved using a similar URL to that used to retrieve a Swagger definition by replacing ‘swagger’ in the URL with ‘openapi’.

There is a new folder in clientserver/server/restful, named openapitemplates. The files in here have the same use as those in the swaggertemplates folder, except that they apply to OpenAPI definitions.

In addition, cors.json has new OpenAPI members that have a similar purpose to the Swagger members.

In versions prior to Studio 10.2, you could provide a format by prefixing a description of a schema field or HTTP method parameter with "<swagger-…>“. In Studio 10.2, you can now provide a format using the prefix of either “<format-…>“ or "<swagger-…>“.

Media types

HTTP responses for a RESTful method can now be defined to return media types other than application/json via a schema.

Note that if you do this, the Swagger 2 definition is incomplete, since Swagger 2 does not allow mixed response content types. However, the new OpenAPI 3 definition for the RESTful service does handle this correctly.

Managing Return Values

There may be occasions where RESTful API remote tasks are not able to generate their content as the return value of the HTTP method. For these cases, content generation can be deferred until later, for example, until a threaded worker object completes, or to allow push support, possibly using server sent events and text/event-stream content. In order to do this, there are additional steps. Before returning from the HTTP method (where you would usually return content):

Calculate $cinst.$restfulapiwillclose as kFalse

This prevents the remote task from closing when you return, and it means that you are responsible for closing the remote task by calling $close() at a later point, or by using the remote task timeout mechanism. Note that it is essential to close the remote task, so that the data connection to the client is closed.

Note that setting $restfulapiwillclose to kFalse will be ignored if an error is detected by the Omnis server as part of request processing.

$restfulapiwillclose has the following definition: If true,the RESTful API remote task will close when the Omnis RESTful HTTP method returns. Defaults to kTrue in a new RESTful API remote task. kFalse only applies when the method executes successfully; you must eventually call $close().

After setting $cinst.$restfulapiwillclose to kFalse, you do not need to return any content, headers or status from the method. If you do return content though, then you also need to set the HTTP status and add any response headers before returning the content. Note that the Omnis server no longer automatically adds the content-length header - this becomes your responsibility if this header is required (in many cases like this it is not).

Sending HTTP Content

The $sendhttpcontent() remote task method lets you send HTTP content back to the client:

When you are ready to generate the response e.g. in a worker callback, call $sendhttpcontent. The xData parameter differs from content returned from a RESTful HTTP method, in that it is always binary (meaning that you are responsible for generating JSON or encoding characters for example).

You can call $sendhttpcontent more than once, to incrementally send content. However, before the first call, you must set the HTTP status and supply the HTTP response headers (including content-length or transfer-encoding chunked if required).

When using $cinst.$restfulapiwillclose set to kFalse, the Omnis server does not attempt to validate the content returned as it does for JSON content when using $cinst.$restfulapiwillclose set to kTrue.

$sendhttpcontent cannot be used in the initial RESTful API HTTP method call. It is intended to be used with an asynchronous worker call.

The $sendhttpcontent() method can be used to send character data (converted to UTF-8 before sending) and list or row data (converted to JSON before sending). In addition, the method has an optional second argument, bChunk, which defaults to kFalse. When true, bChunk formats the data as a chunk (removing the need to call the formatchunk() function). This improves performance a little, and also allows you to handle web servers which automatically chunk the response. A call to $sendhttpcontent with empty data and bChunk passed as kTrue must be used to terminate the content.

Transfer-encoding chunked

You can return content in multiple blocks using transfer-encoding chunked by using $sendhttpcontent. To facilitate this, there is a built-in function:

Each data block to be sent can be sent with code such as:

$ctask.$sendhttpcontent(formatchunk(dat))

These calls need to be followed by a call to send a zero-length chunk (which terminates) the content:

$ctask.$sendhttpcontent(formatchunk())

Server Sent Events

You can use $sendhttpcontent to handle a push connection from a client using Server Sent Events. To do this, set the output type for a get method to text/event-stream. Note: you do not need a content-length header for this. You can then send events to the client using $sendhttpcontent. To facilitate this, there is a built-in function:

For example:

Do $ctask.$sendhttpcontent(formatserversentevent("id",1,"data","my event data"))

The protocol field names in the example are data and id, with values 1 and “my event data” respectively.

Date and Date-time values

Support for date and date-time values has been added to REST-based Web Services support. RESTful services typically use a subset of ISO8601 to exchange date and date-time values, which are supported in Swagger which is used to define web services in Omnis. ISO8601 represents the date or date-time as a character string. See http://swagger.io/specification/ and search for RFC3339 in the page - a link from there takes you to http://xml2rfc.ietf.org/public/rfc/html/rfc3339.html#anchor14.

Swagger has two format specifiers for character string values: date and date-time. The Swagger generator has been enhanced so that for fields (either in a schema or in the HTTP method parameters) of type character, the description can contain a swagger tag that specifies the format, e.g. the description for query string parameter startDate could be “<swagger-date> This parameter is the start date for the requested work”. When Omnis generates the swagger definition for the web service, it looks for these swagger tags, and uses them to set the format for Swagger string types. This has the additional benefit that you can use other supported Swagger string formats, e.g. password and byte.

Dates and date-time values are still exchanged as character values. The application code therefore needs to parse and generate the ISO8601 date and date-time values. To support this, there are two new functions to manipulate ISO8601 dates, or at least the subset of ISO8601 needed to work with Swagger and the Omnis RESTful server:

Note that for a RESTful service, you should always use time zones for input date time values, so you would always pass bNeedTimeZone as kTrue to iso8601toomnis if you are passing bNeedTime as kTrue.

omnistoiso8601() always outputs the timezone using the “Z” UTC time indicator.

Web Services Functions

HTTP Headers

The following functions facilitate using date HTTP header values.

BASE64 encoding

The following functions are for handling BASE64 encoded data. You are recommended to use these with RESTful requests that require them, rather than the functions in OXML.

Cross Origin Resource Sharing

Cross Origin Resource Sharing (CORS) “is a mechanism that allows many resources (e.g., fonts, JavaScript, etc.) on a web page to be requested from another domain outside the domain the resource originated from” (wikipedia). An Omnis RESTful API can handle CORS by implementing the $options HTTP method, and by handling the Origin and other headers when processing other HTTP methods (see above for details). In addition, you can configure the Omnis Server (in both the development and server runtime versions) to automatically handle CORS. This means that the Omnis Server can be configured to automatically send the response to OPTIONS, and to add the correct CORS headers to the response buffer before passing a simple or actual request to the application.

The configuration of CORS for RESTful-based web services is stored in a separate configuration file cors.json which should be added to the Studio folder: there is no CORS configuration if the cors.json file is not present in the Studio folder. (In previous versions the CORS configuration was stored in a “CORS” section in config.json which is now redundant, since it is a separate file that you can edit while the Omnis Server is running.)

There is a template cors.json file containing the required settings located in the templates folder in the Studio folder: you can make a copy of this file and place the copy in the Studio folder, making any necessary changes.

The cors.json file can be changed while Omnis is open, but in this case you need to inform Omnis of the change using the Reload CORS Config button on the server configuration dialog (alternatively, you can restart Omnis). The CORS object (in the cors.json template) can have the following members:

{
    "originLists": {
        "list1": [
            "http://127.0.0.2",
            "http://127.0.0.2:8081",
            "http://localhost1:8081",
            "http://online.swagger.io"
        ]
    },
    "APIS": {
        "*": {
        "origins": "list1",
        "headers": "*",
        "supportsCredentials": true,
        "maxAge": 0
        },
        "Swagger": {
            "origins": "list1",
            "headers": "*",
            "supportsCredentials": true,
            "maxAge": 0
        },
        "OpenAPI": {
            "origins": "list1",
            "headers": "*",
            "supportsCredentials": true,
            "maxAge": 0
        }
    }
}

Note that everything here is optional, and the most likely result of omitting data is that a request will be passed to the application to handle, or a method not supported error will be returned to the client if the application does not implement the method.

A CORS entry has members as follows:

CORS processing in the Omnis server occurs when a request with an Origin header arrives. The server tries to locate a CORS entry for the request. There are two cases:

If the above processing does not locate a CORS entry, then the server does not carry out any CORS processing, and the request continues as it would without CORS. If however the above processing locates a CORS entry:

In order to understand what is going on, there is the "cors" log type that you can specify in the "datatolog" member of the main Omnis "log" configuration. Using this will cause the server to log CORS issues that mean the CORS processing in the server has not handled the request, and is passing it on to the application if possible.

Logging

You can monitor or debug REST requests and responses by adding or editing the relevant items to the “log” member of the Omnis configuration file (config.json) which you can edit using the Configuration Editor. The REST request and response items can be added to the “datatolog” array in the “log” member using the following format:

 "log": {
        "logcomp": "logToFile",
        "datatolog": [
            "restrequestheaders",
            "restrequestcontent",
            "restresponseheaders",
            "restresponsecontent",
            "tracelog",
            "seqnlog",
            "cors",
            "headlessmessage",
            "headlesserror",
            "systemevent"
        ],
        "overrideWebServicesLog": true,
        "logToFile": {
            "folder": "logs",
            "rollingcount": 10
        },
        "windowssystemdragdrop": true
    },

Note: when copying or editing sections in the config.json, you must be careful to include a single trailing comma when required to separate items, i.e. include a comma if another item follows the item you are editing.

Including the “cors” item will cause the server to log CORS issues that mean the CORS processing in the server has not handled the request, and is passing it on to the application if possible.

Further information about Logging in the Omnis Server see Server Logging in the 'Creating Web & Mobile Apps' manual.

Authentication

You must be responsible for setting up authentication in your Omnis library. When using a real Web Server, rather than the built-in web server, you can configure the URL for the web service to support basic or digest authentication. There is also the option of using https, and also client certificates to further secure connections.

The parsehttpauth(auth) function parses the HTTP Authorization header value auth and returns a row variable containing the extracted information. Column 1 of the returned row (named scheme) is the scheme (e.g. basic). Other columns are scheme dependent. Examples for various auth header values:

Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
Digest username="Mufasa",realm="testrealm@host.com",nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",uri="/dir/index.html",qop=auth,nc=00000001,cnonce="0a4f113b",response="6629fae49393a05397450978507c4ef1",opaque="5ccc069c403ebaf9f0171e9517f40e41"
OAuth realm="Example",oauth\_consumer\_key="0685bd9184jfhq22",oauth\_token="ad180jjd733klru7",oauth\_signature\_method="HMAC-SHA1",oauth\_signature="wOJIO9A2W5mFwDgiDvZbTSMK%2FPY%3D",oauth\_timestamp="137131200",oauth\_nonce="4572616e48616d6d65724c61686176",oauth\_version="1.0"
Bearer 0b79bab50daca910b000d4f1a2b675d604257e42

Web Server Configuration for Authentication

If you want to setup Basic and/or Digest authentication for your web services running on Apache Web Server or IIS, please refer to the tech notes section on the Omnis website.

Manipulating Resources

The server can use the same mechanism for manipulating resources as the client, so refer to the sections above. Configuring and logging the Omnis RESTful API server is achieved in the Omnis configuration file (config.json).