RestFul server with Harbour

José Luis Sánchez
Harbour Magazine
Published in
8 min readNov 29, 2017

This article is a translation from ‘Servidor RestFul con harbour’ written by Rafa Carmona. Any mistake in the traslation is only mine, and I apologize for it.

At the meeting celebrated in Novelda, Spain, I didn’t have the opportunity to show the potential of having a RestFul server in a simpler state. This was probably due to the time available and that basic knowledge is important to understand the different parts that make up the RestFul server and time was pressing.

As I had to split the conference in two parts, one for the web server and other for the RestFul server, I hadn’t enough time to explain all the details.

I have to say that the content of the post will run on Harbour 3.4, because with latest changes we can select which verbs or request methods are going to handle. But don’t worry, we are going to hack Harbour 3.2 in core.prg so you can enjoy it ;-)

Although, it will not be a ‘pure’ WS, but this don’t have to worry us a lot, because our goal is to fulfill our needs. In this example we are going to create a fully functional RestFul server, and we will learn to make a CRUD over a DBF table.

I have to say, these are differences to other languages, and in its conception was to act as a web server, does not perform the assembly automatically when we want to do things like http://client/1/invoice/10, and it will be that our responsibility to perform and parse it.

For this we are going to assemble a couple of classes that will extract us from the 'supposed' complexity of handling the client's requests. For customer requests I will use the Postman tool, for the sake of convenience.

But for that, I'm going to show you how to do it so that anyone can implement it in their projects, here is the sketch we will work on, which in the end will be slightly different, but not much.

Inspiring sketch

Let’s start with BaseController class. This class will manage the requests that come from the server hbhttpd.

This class will be responsible for making requests to the object, through the corresponding oService. And how do it know where to call?

The explanation is that the class that implements the service must inherit from another class of the BaseService type. This class implements a series of methods, which must be overwritten in the parent class. But do not worry about it now, we will see it in the examples and you will understand right away.

Following with the BaseController class, we will explain which is the methodology of implementing the controllers. The basic idea is that we can use the BaseService type, either if we are going to use a server or not.

BaseController Class

If we take a quick look, we have a fairly simple class. Do you expect anything more complex ? ;-) Let’s start.

Controlamos que el servicio este instanciando

New ( cID ) method receives as an argument the ID, that can be empty. In addition, it is indicated, through UAddHeader, that the return message is the JSON type, and following calls the :: controller (cID) method. This method is responsible for making the corresponding call according to the received http verb. And a tip: if we don’t have instanced the service we will receive a 412 error, this way we will quickly locate if we have forgotten to create the service.

Determining wich http verb to process

Here all we do is determine what type of verb comes to us, GET, DELETE, PUT, POST. Now we will see each one.

The case of the GET is special, may or may not come the ID. If this is empty, it means that it has been launched from http://127.0.0.1/clients, for example. If the ID came, it would be http://127.0.0.1/clients/120, where ID = 120.

In the case of PUT and POST, what is done is to discriminate the Content-type, in this case we will only allow JSON to be received, otherwise, we will return an 415 http error. In addition, we will send the content of the BODY_RAW, in hash form.

El resto de métodos

With this methods we have our class ready. These methods call the corresponding method of the service previously instantiated in the class that inherits from this class. Note that the findAll() method is sending to the servide the offset and limit, that we will retry from the URI, so we can filter and page the queries.

Are you impressive with the simplicity ? ;-) Wait, you’ll still see what the parent class remains simple.

Now we will see the other class, BaseService, that will serve to complete the base on which we will work later.

Oh, another surprise, simplicity.
We have to keep in mind that the uValue variable is the one that we will feed from our service class.
The variable lError will be used to indicate if an error occurs, especially it is important to determine in the instance of our class, if any table opening, etc .., indicate it, so that the controller subsequently act accordingly.

Method to override to gain functionallity

By default, if we receive an http verb, for example, DELETE, and our service does not support DELETE, it will simply inform you with a 501 error that is not implemented.
Well, these two simple classes are what will save us a lot of hours. And the question is: how to use them?
The first thing will be to create our class, StatusController.

That’s all. The only thing to keep in mind:

  • Inherit from the BaseController class
  • New method receives an argument called cID
  • Instance our Service in the oService variable
  • Call ::Super:New(cID) to process the request

At this point, before continuing in the implementation of our service, StatusServiceAPI, we will see how we can change the operation that it has by default, for example, findAll().

If we remember, findAll(), it receives by default the offset and the limit. We will be responsible for the service to obtain these values and establish our rules, whether in SQL, DBF, etc …

In the case that, by default, we receive a third parameter or another series of parameters, we simply get it overloading the method in our class that interests us, in this case my findAll() will support more parameters apart from the offset and limit.

Logically, we will have to put memvar get, to access the public variable get from the RestFul server. This is all we have to do in our controller class.

We are going now to finish our service class that was pending, StatusServiceAPI;

Override of the methods that we are going to develop que vayamos a implementar

Let’s start creating a record, using Postman tool;

Lookingat the image, we have in one side;

Detalle

On the POST selector, we will select the corresponding HTTP verb, in our case we’ll select POST to create a record.

We have also the URI what we are calling ;
http://127.0.0.1:8002/v1/statusType

In the other side, we select Body, raw and JSON(application/json) and put the JSON that we will send.

If everything goes well we get the status and the time elapsed to run our request, in this case, we get a 201 message to indicate that the record has been created.

And how does it do ? Here is the code:

The Create() method is called, which receives the Hash, and we insert it. In this example, I have tried to apply possible errors, such as 409, which is trying to create an element that already exists. Remember this has to work as best suits you, trying to keep a criterion as close to what others do.

Finally, comment on the findAll () method. The call from the Postman;

It returns and array of objects status

Notice the pass of arguments, and what you see, is the result of the second record, offset moves a record in the dbf and limit forces to take only one. As always here the code:

I think that it’s enoght to see haw to create a simple server, you have all the code available at GitHub;

https://github.com/rafathefull/restful

The new thing about this system is that by having separated the service from the controller, we can use the service directly, independently if it is running on a web server or not.

Another advantage is that we can debug our service without having to run a ws.

Well, once we have seen the essentials of the system of controllers and services, we will see how we started this, the last thing that we have left ;-)

Notice how we manage 2 requests, one with an argument and the other without it, there isn’t anything else !

Ah! By the way, I forgot, for harbour 3.2, just see in contrib/hbhbtppd/core.prg

In STATIC FUNCTION ProcessConnection() look for these lines and add the one on bold:
server := hb_HClone( aServer )
get := { => }
post := { => }
server[ “BODY_RAW” ] := NIL

In STATIC PROCEDURE ParseRequestBody( cRequest ), add the line in bold:
LOCAL nI, cPart, cEncoding
server[ “BODY_RAW” ] := cRequest

Well, now just have to put the verbs, look for GET POST and you’ll find this line;
ELSEIF ! server[ “REQUEST_METHOD” ] $ “GET POST”

then add the new supported methods;
ELSEIF ! server[ “REQUEST_METHOD” ] $ “GET POST PUT DELETE”
build again the lib and it’s ready.

I hope this article gives you the opportunity to grow your applications and also Harbour.

--

--