In this two part tutorial you will see how easy it is to create a basic web service using Xojo Web. In the second part, we will create a Desktop client to talk with the web service (you may want to add iOS to the list).
Before we start, let me point out that this tutorial leaves out some details related to error checking, validation, data sanitizing and other specifics related to inputs and outputs in order to focus on the central topic. In addition, if you want to follow and reproduce the steps of this tutorial, then you need to download the Chinook Database, named “test.sqlite” in this tutorial.
Services in practice
Let’s start creating a web service! Open the Xojo IDE, create a new Web project, and name it
WebService. The first thing you’ll notice is that Xojo adds a web page to the project by default – even when our web service does not display a UI. The good part is that you can mix both worlds, adding the webservice part to your regular Web app!.
In fact this tutorial will put all the logic in the App object. Start by adding a new propertyin charge of the reference to our SQLitedatabase (of course, it will work also with PostgreSQLor MySQLengines). Select the
Appobject from the Navigator(the left panel in the Xojo IDE), choosing the
Insert > Propertyoption from the contextual menu. With the new property selected, go to the Inspector to set the name, type and scope using these values:
- Name: Database
- Type: SQLiteDatabase
- Scope: Private
Now is time to create a new SQLiteDatabase instance and assign it to this property, so it will point to the SQLite database file when the app is running. For that, make sure the
Appobject is selected and add the
OpenEvent Handlerto it using the
Insert > Eventoption. Write the following code in the resulting Code Editor:
dim f as FolderItem = SpecialFolder.Desktop.Child("test.sqlite") if f.Exists then database = new SQLiteDatabase database.DatabaseFile = f call database.Connect end if if database.Error then MsgBox "Error connecting to the database"
As you can see, it is pretty much the same code we already use when creating SQLiteDatabaseinstances in our Desktop apps, linkingto our SQLite database file and stablishing the connection so we can operate with the database engine from our app.
All the Magic of HandleSpecialURL
Xojo Web projects offer two ways to handle the request received. The first is using the
HandleURLevent. This is the one that fires every time a client app (it may be a Web Browser, desktop or mobile app) connects to the URL associated with the IP address and port combinationthat is listeningfor incoming requests. If we were writing a Web App, then this would be the event we would use to attend, process and send the requested web pages, for example.
However, for web services we need to add the
HandleSpecialURLevent to our project. The main difference is that, in this case, we can use typical URL signatures when its about talk with web APIs, where the methods(i.e, the requested action by the clients or API consumers) is part of the URL itself. The unique consideration is that these URL need to include the word Specialor APIas part of their URL path. In addition, this is the event that will allow our Web apps to attend and process the callbacksfrom other external web services, as could be PayPal, for example.
For example, a valid URL that can be trappedand processed by
getCustimersis in this case one of our API methods.
So, with the
Appobject selectd, choose
Insert > Eventin order to add the
HandleSpecialURLevent, as is shown in the following picture:
As we will see, once the event has been added to a web app, it will receive the
WebRequestdata type), waiting from us to send back a
Booleanvalue as response:
Trueto process the request or
False(the default value) to ignore the response.
Introducing Request, where the information lives!
In fact, we will find in the
Requestobject everything we need to process and (if it is the case) respond to the request from our Web service. For example, through we can get the interesting component from the
Pathproperty, excluding the
/api/from it, as it is also the case for the
?symbol that could be included as part of the complete url path. For example, if we consider this URL:
Request.Pathproperty will return the
getCustomersstring; so our web service can process it acordingly from this point on.
Receiving and Sending JSON data
In order to keep this tutorial brief, our web API only has two methods in it:
AddAlbum. Using the first one, the client app will get the album name in the database wrapped in JSON format. With the second method, our client app will ask the web service to add a new record (a new album) to the right table on our example database.
How can we process the request associated data inside the
HandleSpecialURLevent? Here is where we will find very useful another of the
Requestobject properties. The
Entityproperty includes the sent data as part of the request that are not already present in the headers. Generally speaking, it includes additional data via the PUTand POSTverbs.
Now we can put the following code into our
dim data as string = Request.Entity.DefineEncoding(encodings.UTF8) // We have to apply the right encoding to the received data dim input as JSONItem = new JSONItem( data ) // Creating a new JSON object from it dim output as JSONItem Select Case Request.Path // What is the method received as part of the request? (URL) case "GetAll" output = GetAllAlbums // Assign the processed data to the output variable, if the received method is 'GetAllAlbums' Request.Print( output.ToString ) // And send it back to the client that made the request, converting the JSON to a String in first place case "AddAlbum" addNewAlbum( input.Value("newAlbum") ) // In this case, the request is to add a new Album to the database; passing thus the received data as part of the input end select Return true
The main point here is that we assign the received data to the
datavariable (if any), and define a known encoding to them so we won’t get into trouble while processing them afterwards. Of course, our example always expects to receive the additional data in JSON format, so this is why we create a new JSON instance from this data.
We use the
Select…Casecommand to decide what method has to execute the web service, and that is based on the component stored in the
Pathproperty as you recall. So, if the request uses the
GetAllmethod in the URL, we will call the real method
GetAllAlbumsin our Xojo code. After processing the information it will return a new
JSONItemas the response we will send to the client.
How can we send the response to the request? Really simple: calling the
JSONItemreferenced by the
If we receive a request with the
AddAlbummethod of our API, then we call the real
addNewAlbummethod in our Xojo code, passing as parameter the
JSONItemobject in charge of store the received data from the request (this is the one referenced by the
inputvariable). In fact, the record structure is stored inside the
newAlbumroot node of the JSONItem.
When it comes to the Database
HandleSpecialURLis in charge of processing the received request, we will use a couple of methods in our example app that will act as a linkbetween the API and the database in the backend, both for retrieving and storing the requested information. (In a real world app it is very advisable to introduce checks and data sanitization before dealing with the database!)
Appobject again and use the
Insert > Methodoption in order to add the
GetAllAlbumsmethod, using the following method signature for that:
- Method Name: getAllAlbums
- Return Type: JSONItem
- Scope: Private
This is the code in charge of generating the JSONitem that we will print as part of the request response, including the nodefor every expected database record from the
Albumtable in our example database:
dim rc as RecordSet = database.SQLSelect("Select * from album order by title asc") // Get the Recordset as result of the SQL selection: all the records dim item as new JSONItem if rc.RecordCount > 0 then // We have records on the RecordSet While not rc.EOF // so lets iterate them! dim d as new Dictionary // creating a new dictionary for each record, and that we will convert in a node d.Value("artistid") = rc.Field("artistid").StringValue // assingning the record ID to the name 'ArtistId' of the JSONItem d.Value("title") = rc.Field("title").StringValue // and the Title value to the 'Title' field of the JSONItem item.Value(rc.field("albumid").StringValue) = d // You know you can assign a Dictionary as the value for a JSONItem node. Very useful! rc.MoveNext Wend rc.Close end if dim output as new JSONItem output.Value("AllAlbums") = item // Then let's hang all these records form a main Node Return output // And return it to the caller
Next, create a new method named
addNewAlbum. This is the one our web service will use to add a new record to the database, using for that the received data as part of the request. Use the following signature for the method definition:
- Method Name: addNewAlbum
- Paramters: item as JSONItem
And put the following code in the associated Code Editor:
dim title as string = item.Value("Title") // get the data associated to the "Title" field dim artistid as string = item.Value("ArtistId") // and the 'ArtistID' database.SQLExecute("insert into album(title,artistid) values('"+title+"',"+artistid+")") // and insert that data as a new record into the database table
As you can see, the code is very simple (and dangerous in a real world app — prepared statements should instead be used!): it gets the values for the received keys in the JSONitem nodes and uses them as part of the SQL sentence in order to add a new record to the database.
A Web service… ready to serve!
As you have seen, the code and app structure are really minimal! Of course, this is just a simple example but it gives you a good idea of the kind of possibilities web services offer and how fast you can put it together using Xojo and OOPconcepts you already know! Of course, you can run your web app (and services) right from the IDE… what is probably the recommended way to follow this simple example. For other more complex web apps, remember that you can use the one click solution Xojo Cloud and of course any compatible Linux, Windows or Mac server.