Web services are everywhere. Without noticing, you and I use them daily. Perhaps you have already implemented web service functionality in your Uniface applications? Building a web service in Uniface is easy, but is it possible to build a RESTful service in Uniface?
As I showed in my previous article, building a web service in Uniface is easy with the following steps:
- build a normal Uniface service component
- allow an operation on this Uniface service to be accessible by the web by adding the public web statement
- returning the result with the $webinfo(“output”) function.
This results in the simplest web service that meets the specifications. Turning this into a RESTful service we need to add a bit more:
- Setting the correct Content-type in the HTTP header and
- the correct http status.
In this article I will explain these steps in more details. In the last paragraph I will give an example how to use input parameters for your RESTful service.
Why is it important to use the protocol as it is intended?
A generic definition of a web service could be a “service offered by an electronic device to another electronic device, communicating with each other via the HTTP protocol”. It is about two (or more) systems that communicate with each other.
In that last article, I showed how easy it is to call a Uniface service over the web. A very basic “hello world” only takes four lines of code. The point of that example was just to show how easy it is to return something to the requesting system. Since the purpose of a web service is to communicate between two systems without any user interaction, the syntax of the content is not very interesting. Or is it?
The main requirement is for both systems to know what information is being transferred, and how. Just as when humans are talking to each other, it is important to have a few guidelines. One of these is the syntax or grammar that is used: let’s call it a language. As long as I understand the language the other person is using, I can process the information I receive (by listening). As soon as the other person starts using a language I don’t understand, I can still hear them talking, but I don’t understand what I hear.
Communications between systems are no different. There must be some kind of “agreement” about communication. This agreement is the protocol that is used. We need to agree on two protocols, one for the content and one for transportation between the systems. In a web environment, the HTTP protocol is used for transportation. In the case of a RESTful web service, the protocol used for the content can be anything.
Let’s consider both protocol requirements in more detail and see how Uniface helps.
Using the correct Content type
Life is easy for us Uniface developers since Uniface does most of the work for us. I always feel sorry for developers who do not use Uniface. The most basic web service I can think of – the one we discussed in the previous article – only uses a few lines of code:
operation Hello_World public web $webinfo("output") = "Hello World!" return (0) end
A RESTful service can have any type of content you like, from an image to plain text (like in the “hello world” example) and anything in between. In most cases, the content will contain information in XML or JSON. Both are very well supported by Uniface. Just replace the $webinfo(“output”) value with the XML or JSON stream you created in Uniface.
My advice is to keep it as simple as possible and let Uniface do the work for you, creating an XML or JSON from a struct and creating that struct from the entity structure.
This “Hello_World_json” service is a very basic example:
operation Hello_World_json public web variables string vj_data struct vu_data endvariables my_message.MY_ENTITY = "Hello World" componenttostruct vu_data, "MY_ENTITY" structtojson vj_data, vu_data $webinfo("output") = vj_data return (0) end
When I start this operation from a browser, it returns:
This is valid JSON, but as soon as this JSON is sent by HTTP there is something missing. Uniface can not guess what you are trying to sent. You must specify the type of content in the HTTP header. The correct value for the content type is application/json. The content type is an element of the HTTP header.
In Uniface, this can be set by using the function $webinfo(“HttpResonseHeaders”). This is an associated list of all elements in the HTTP header, to be set by the developer. To set the correct content type of the response, we need to add a line to the example:
putitem/id $webinfo("HttpResponseHeaders"), "Content-type", "application/json"
Setting the correct HTTP status
Remember that a web service is used to communicate between systems. As long as both sides work in an agreed manner, everything is fine.
This hello world example is a web service: maybe not a very realistic one due to its simplicity, but still it complies with the rules. And again, most of the work is done for us by Uniface. Of course, not everything is done automatically – for example, describing the content that is the subject of our development. If we create a certain type of content, we must specify that in the header of the response.
Besides the type of content, it is also important to communicate the status of the request. Could the request be executed? Was the request well-formed? Was the response read from cache? Is the requesting system authorized? A lot of things can go wrong, and it is important to notify the requesting system if they do. This is done via the HTTP status. There is a whole list of possible HTTP status codes. Check out the site of Mozilla for instance.
There’s nothing new here, I hope. We’re talking about the 404 status when you request a page that cannot be found on the internet, or the 500 when the server returns a technical error.
The most important status is 200, which means a simple “OK”. When Uniface can return anything, it will set the HTTP status to the value 200. It is up to us to change this when, according to your software, anything is not “OK”.
Let’s take a more sophisticated example in which data is read from the database. For a Uniface developer, that is just routine. As long as there is data to return, the HTTP status is 200. As soon as there is no data to return, the HTTP status is set to 204.
The HTTP status can be set (to 204 for instance) in Uniface by:
$webinfo("status") = 204
Use query parameters to send data to the webservice
An operation that acts as a web service in Uniface cannot have any parameters – at least, not as we know them in Uniface. I’m talking here about the parameters specified in our coding between the params and end params statement. Of course, you must be able to send information with a request that will act as an input parameter. There are two possibilities: send it in the request body itself or as a query parameter in the URL.
All information in the request body can be interpreted by Uniface. It is like our “Hello world” example, but the other way around. Convert the received JSON or XML to a struct and convert that struct to the component structure.
In this article, I want to focus on the second way of sending parameters with the HTTP request: adding the parameters to the URL. This can be easily tested in a browser, and is, in fact, something you use every day in your browser since the whole World Wide Web relies on this kind of parameter exchange. This is because the HTTP method is GET. This is very important to note since the RESTful protocol is based on the HTTP method. In the next article, I will focus on the HTTP protocol and how to use it with your Uniface services. For now, it is enough to use the GET, since this is the default in the browser.
In the GET HTTP method, the parameters are added to the URL. These are called query parameters. They appear in the URL after the question mark (?) that follows the resource name, and are separated by an ampersand (&):
In Uniface, $webinfo(“input”) carries the parameters and their values in an associated list.
Let’s enhance the “Hello world” example to demonstrate all of the above. The new sample should accept two parameters:
- PLACE: this parameter contains a text that is processed. In this small sample, it is returned in the response, preceded by the word “hello”. If this parameter is empty or not given, the word “world” is used.
- TYPE: this parameter can have two values, XML and JSON. This specifies the protocol the response is using. If this parameter is omitted or has a nonvalid value, an HTTP status of 400 is returned.
This operation might look like this:
operation hello_wherever public web variables string vj_data struct vu_data string vs_place , vs_type xmlstream vx_data endvariables vs_place = $item("PLACE", $webinfo("input")) if (vs_place = "") vs_place = "world" endif vs_type = $item("TYPE", $webinfo("input")) my_message.MY_ENTITY = $concat("Hello", vs_place) componenttostruct vu_data, "MY_ENTITY" if (vs_type = "XML") structToxml vx_data, vu_data$webinfo("output") = vx_data putitem/id $webinfo("HttpResponseHeaders"), "Content-type", "application/xml" elseif (vs_type = "JSON") structToJson/whitespace vj_data, vu_data $webinfo("output") = vj_data putitem/id $webinfo("HttpResponseHeaders"), "Content-type", "application/json" else $webinfo("status") = 400 endif return 0 end
The query parameters should only be used to retrieve something from the other system. They should never be used to send new or modified data. Let’s recall that REST means “REpresentational State Transfer.” In other words, with the query parameters, only the current state of a resource should be retrieved. They must not be used to change the representation.
There is a close connection between RESTful operations and HTTP methods. The most common methods are: GET, POST, PUT and DELETE. In our example, we have used the GET.
Using these RESTful operations together with your Uniface services is one of the most interesting parts of implementing true RESTful web services in your application. In the next and final article of this series, we’ll look at an equally interesting aspect: the best way of implementing HTTP methods.
This article is also published (with a few modifications) on the Uniface website.