In a previous entry we started to dig into web services with Xojo. The first post focused on the backend (server side), creating the Xojo app acting as middleware between the clients and the database that holds your data. We are using SQLite as the backend engine but it would not be difficult to change to other supported database engines like PostgreSQL, MySQL (MariaDB), Oracle or SQL Server, and even ODBC; all of these are supported by Xojo!
In this second post on the topic we will focus on the client side desktop app that will be in charge of creating the requests using the web service published API. This desktop app will send new data to our web server app so it can be added to the backend database.
The main topics are:
- Creating a subclass from the
- Dealing with the data received from the server as result for the request
- Encoding a
MemoryBlockfor sending data to the web service
As stated in the previous entry, the example doesn’t include the normal (and recommended) verifications on the data provided and received or the check on possible errors; we are only focused on the main topic.
Creating an HTTPSocket subclass
Xojo provides two classes in order to create requests using the HTTP protocols: HTTPSocket from the original Framework, and Xojo.Net.HTTPSocket from the Xojo Framework, where the use of namespaces is enforced as is the case in other programming languages.
Both of these classes are subclasses from the
TCPSocket class, but only Xojo.Net.HTTPSocket supports the HTTP 1.1 version of the protocol, while
HTTPSoket only supports HTTP 1.0. For this tutorial we will use the
Xojo.Net.HTTPSocket; not just for the more convenient HTTP 1.1 support, but also for other advantages.
Let’s start by creating a new desktop project. Add a new Class using
Insert > Class and use the Inspector to assign
Xojo.Net.HTTPSocket as the Super Data Type as shown in the picture:
With the just added class still selected in the Navigator (the leftmost column in the IDE Window), add a new property to it (
Insert > Property). We will use this property so it points to a ListBox in our user interface, and so we can update it with the data received from our web service. With the just added property still selected, use the Inspector to set its Data Type and Name (text label used from our code to refer to such property) as follows:
- Name: List
- Type: ListBox
- Scope: Private
Note that we set the scope as Private. This means that this property can be accessed only from the instances of the class, and not those that are created from other data types or even those created as subclasses from this class.
HTTPSocket: Asynchronous communications
Why would we want to maintain a reference to a ListBox control in our HTTPSocket class? As with any kind of problem, we can take several approaches in order to solve it. In this case, is a very convenient way to get a reference to the control we want to update as we will be using the HTTPSocket asynchronously.
Once we call the request against our web service, the execution of the app will not be paused until we receive a response, this allows the user to do other operations if they want. In this case the class instance, via the
PageReceived event, will be responsible to receive the new data from the web service, so we can use it.
In fact, this is our next step. With our
Xojo.Net.HTTPSocket still selected in the Navigator, add a new Event Handler to it (
Insert > Event Handler…). In the resulting window, choose the
PageReceived option from the list and confirm the selection, so the event will be added to the class under the Event Handlers section.
Now, with the just added Event Handler still selected, write the following snippet of code in the associated Code Editor:
if httpStatus = 200 then // We received an OK as the status for our request if Content.Size <> 0 then // And we have data to process as part of the reply list.DeleteAllRows // So we delete previous existing entries in the associated ListBox Control dim data as text = Xojo.Core.TextEncoding.UTF8.ConvertDataToText(content) // We set the encoding for the received data dim items as xojo.core.dictionary = xojo.data.ParseJSON(data) // And proceed to create a new dictionary from the received data in JSON format items = items.Value("AllAlbums") // We get all the items for the key 'AllAlbums'… for each item as xojo.Core.DictionaryEntry in items // …iterating on them… dim tempDict as xojo.core.Dictionary = item.Value // …and retrieving the dictionary (JSON item) for it… dim title as text = tempDict.Value("title") // …so we finally access the desired value list.AddRow title next end if end if
The code is commented so you can follow what happens in each line in the process; but it is worth it to point out that the
PageReceived event gives us all the parameters we need in order to work with the data sent from the web service:
- URL: The URL returned as the reply for our request
- HTTPStatus: The HTTP status code that you should read to verify the success of the request
- Content: This is a MemoryBlock that can contain the data associated with the reply in binary or text forms
An additional interesting detail is the use of the
Xojo.Core.Dictionary class instead of the original
Dictionary class. The main advantage in using the first one, among others, is the ability to easily iterate among its contents using an iterator (
In addition, you can also add an extra event handler to our subclass:
Error. This will be fired when an error is produced while dealing with requests sent from the instance.
With the events and property already set, it is time to add a couple of methods in charge of commanding our HTTPSocket to launch the requests. For our example, those method names are directly related to the operations we want to do on the web service API created on the first part of this tutorial.
So, with our HTTPSocket class still selected in the Navigator, add a new method (
Insert > Method), using the following data in the associated Inspector. This is the method in charge of requesting our web service for the name of all the available albums. Of course, we expect to receive the reply through the
- Method Name: GetRequest
- Parameters: theList As ListBox, theURL as Text
- Scope: Public
As you can see, this method expects to receive the reference to a ListBox control, and the text to the URL that should be the end point of our web service. Now put the following code in the associated Code Editor for the just added Method:
list = theList self.send("GET",theURL)
Simple, isn’t? As you can see, we save the reference to the ListBox control in the instance property so we can use it later for updating its contents with the received data. Then we use the
Send method from the class with the
GET verb and the URL where our web service should be listening for incoming requests. As previously stated, this is an async operation, so we will let our object to react through the
HTTPSocket: Sending data as part of a request
Add a new method to the subclass, using the following values in the resulting Inspector. This is the method allowing us to send data to the web service so they can be added to the backend database:
- Method Name: SendItem
- Parameters: Item As JSONItem, theURL as Text
- Scope: Public
This time, the method expects to receive and JSONItem object (pointed via the variable
Item) and a Text instance that should be the web service endpoint URL. Put the following code in the associated Code Editor for the method:
dim d as xojo.Core.MemoryBlock = xojo.core.TextEncoding.utf8.convertTextToData(item.ToString.ToText) // Convert the data to send to the expected data type as memoryblock self.SetRequestContent(d,"application/json") // attaching the data to the request with the MIME type self.Send("POST",theURL)
The HTTPSocket instances allow us to associate additional information (data) for a request sending, in addition to those added through the HTTP headers (
Head), using for that the
SetRequestConent method. This method expects two parameters: a memory block with the real data to attach, and the definition of the type of data we are sending (this is, the MIME Type) as a Text instance.
So, in the code we create the memory block from the JSONItem passed as one of the input parameters, and that is in fact the data structure for the record we want to add to our backend database. The second line of code just attaches this data to the request and, finally, we launch the request in the last line of code, using for that the URL assigned to the
theURL parameter and using the verb
Designing the User Interface
We have already finished all the logic behind our example app, via our HTTPSocket subclass. So now it’s time to design a minimal user interface so we can verify the correct functionality of our client agains the web service.
Window1 object and add the required control so it looks like the one displayed here (from top to bottom): a ListBox, a couple of TextFields and two PushButtons named “Request” and “Send”.
Next, add an instance of the created HTTPSocket subclass. For that, simply drag the created subclass from the Project Browser to the Window Layout Editor and a new instance will be added to the Tray in the lower section of the Editor, as shown in the following picture:
The only code present in the user interface is the one that we will add to both PushButtons. Double click the one labeled “Request”, and add the following line of code to its
Action Event Handler:
It calls the
getRequest method on the instance based on our subclass, sending as parameters the control “ListBox1” and the text for the (local) URL where the web service should be listening.
Double click the PushButton labeled “Send” in order to add the
Action Event Handler and write the following fragment of code into the associated Code Editor:
dim d as new Dictionary d.Value("Title") = TextField1.Text d.value("ArtistId") = TextField2.Text dim item as new JSONItem item.Value("newAlbum") = d MySocket1.sendItem(item,"http://127.0.0.1:8080/Api/AddAlbum")
As you can see, there is nothing magic in it. We simply create a new dictionary using the keys expected from our web server to access the underlaying data for the record. In addition, you can see how easy it is to create a new JSONItem from a Dictionary!
Then, we simply call the
sendItem method on our HTTPSocket subclass instance, passing along the expected parameters: a JSONItem instance and the web service endpoint URL that should include the method we want to call in the remote API.
Ready to listen for your requests!
We have finished our client app! Launch the web service created in the first part of the tutorial. Then, run the desktop client app and push the buttons for “sending” or “requesting” the information. You will see how with the request you’ll receive all the albums available in the example database, while with the “Send” button you’ll be able to add new records to the database with the data provided in both TextFields.
In conclusion, Xojo allows us to create both the backend and the multiplatform clients in record time and in a really easy way! Obviously, the examples used in these two articles are not, by far, complete solutions you can use “As Is”, but they give the basics you can use to construct more robust and complete solutions for your own needs.
Watch this video en español.
Javier Rodriguez has been the Xojo Spanish Evangelist since 2008, he’s also a Developer, Consultant and Trainer who has be using Xojo since 1998. He manages AprendeXojo.com and is the developer behind the GuancheMOS plug-in for Xojo Developers, Markdown Parser for Xojo, HTMLColorizer for Xojo and the Snippery app, among others