AL support for REST Web Services

24 Jun

A few days ago we did a webinar about the current status of AL in Visual Studio Code. The webinar was organized by NAV Skills and together with Waldo, Erik Hougaard and Daniel Rimmelzwaan we had a full hour of content. And it really felt like we just got started. I had the pleasure to talk about the support of web services in AL language. If you want to watch the recording, then go to the video on Youtube.

The code (including some improvements) that I demonstrated during the webinar is now available on my GitHub repository: https://github.com/ajkauffmann/ALCodeSamples. See the folder “GitHub AL Issues”. Feel free to clone, branch, fork and play with the code.

The code will only run on the June update of the Dynamics NAV Development Preview.

Built-in support for web service calls

With AL code we don’t have support for .Net components. If you want to know more about that, I can recommend to read this blog post from Vjeko. Instead of using .Net components for calling web services, AL has built-in types that map to .Net types. At the moment of writing AL supports:

  • REST web services
  • Handling JSON

Calling REST web services is supported by the HttpClient class. This is basically the same .Net class I have been using in the web services examples that I posted earlier. In this post I introduced the HttpClient. Now we see the same type in AL as a built-in type. This makes a lot of sense and calling web services is a breeze.

For handling JSON we have a set of four types: JsonToken, JsonArray, JsonObject and JsonValue.

The official documentation can be found here: Developer Reference on MSDN. Scroll down to REST API and you will find a list of all supported objects.

With this post I want to introduce a basic example of using HttpClient and JSON objects. The example reads the list of issues from the GitHub repository of the AL language and stores it in a table. Instead optimizing the code I have created just one Codeunit that calls the web service, reads the Json and stores it in the table.

Let me shortly go over some code lines. Here you see the variables I’m using.

ALSupportForRESTWebServices-Variables

As you can see, I’m not using all of the Http classes, only the HttpClient and HttpResponseMessage. On the contrary, all Json classes are used in this example. Well, actually I could avoid some of the Json classes, but I used them to demonstrate their purpose.

HttpClient

Calling the web service is simple and straightforward:

ALSupportForRESTWebServices-CodeExample-1

On line 18 we start with setting a request header called ‘User-Agent’. Without this setting, the call to the web service will fail. This is due to the fact GitHub just rejects calls where this header is missing.

The property DefaultRequestHeaders (documentation on MSDN calls everything a method, but actually a lot of them are properties) returns the class HttpHeaders. This class has a method Add which I use to add that header. It is not needed to first store the HttpHeaders object into a separate variable and then call Add on that variable. As you can see in the code, the Add function is called directly. This kind of code combining is possible since the June update.

The Get method of HttpClient accepts an url and an HttpResponseMessage in which it returns the response message. The Get methods is basically the HTTP GET method and corresponds to opening an URL with your browser. A simple test to see what the web service is returning would be to open the URL in Chrome or Edge. These browser will show (in most cases) the response directly in the browser window.

The Get method returns a boolean that indicates if the call to the web service was succesful. But be careful here, it only indicates if the domain (the base url, in this case https://api.github.com) could be reached and if the call was not rejected by the host. Try this code without the User-Agent header, and the Get will fail.

In case the host could be reached but the path (the remainder after the base url) was incorrect, then it will be indicated in the HttpResponseMessage.

HttpResponseMessage

Let’s have a look at the HttpResponseMessage.

ALSupportForRESTWebServices-CodeExample-2

In line 24 you can see how to test if the web service call was successful. The IsSuccessStatusCode actually checks if the HttpStatusCode is 200, which indicates a successful call. In case the call failed you may find more information in the ReasonPhrase property.

After this, we can get the content of the response. The Content property returns a HttpContent object. The content contains both headers and a body. Because we are only interested in the body we call directly the ReadAs method on the Content property. In this example we use the JsonText variable (a text variable) to indicate the we want to read the content as text. The ReadAs method also accepts an InStream object which enables to receive binary responses as well.

Next step is to parse the JSON response value.

JsonArray

Our first step is to get read the JsonText variable into a JsonArray object using the ReadFrom method.

ALSupportForRESTWebServices-CodeExample-3

 

Why do we use an array here? Well, that depends on the structure of the JSON response. This is something you need to find out before you write the code to handle the JSON. In this case, the JSON structure starts with an [ which indicates it is an array.

ALSupportForRESTWebServices-CodeExample-4

In the array every item is an object. That is indicated by the curly bracket you see on the next level in the JSON text.

On line 37 we start a loop through all objects in the array. At this moment foreach is not supported in AL, so we have to use the good old for … do structure.

The JsonArray.Get method returns a JsonToken. This is a generic container object that represents a well-formed part of JSON data. Basically every JSON object can be represented by a token object. Because we know that every item in the array is an object, we convert the JsonToken to an JsonObject with the function AsObject.

Now we can start reading the properties from the JSON data and store them in the database.

JsonObject and JsonValue

The JSON data contains arrays, objects and values. You can recognize them by in this way:

  • Array is surrounded by square brackets: [ ]
  • Object is surrounded by curly brackets: { }
  • Value has format “key”: “value”

In the JSON data we are using in this example, every GitHub issue is a JSON object. In the JSON object we find values like number, title, state, etc. To get a JSON value out of the JSON object, we use the JsonObject.Get method. This method returns true if the operation was successful. You can see that on line 42 below.

ALSupportForRESTWebServices-CodeExample-5

Note that the Get method returns a JsonToken. This is because a JSON object can contain values, nested objects or arrays. The JsonToken can represent any of them. Next step is to convert the JsonToken to a JsonValue and read the value. In line 45 you can see that we use the AsValue method on the JsonToken and directly call the AsInteger method on the returning JsonValue object.

This is what makes the JsonValue object so powerful: it exposes methods to read the value as integer, as text, as boolean, etc. At the moment of writing the AsDateTime is not working, that’s why line 49 is commented out in the code.

To avoid writing the two lines of code on line 42 and 43 over and over again, I have created a function GetJsonToken that returns the JsonToken or throws an error if not found.

ALSupportForRESTWebServices-CodeExample-6

In line 50 the code uses another feature: selecting a token based on a token path. The function looks like this:

ALSupportForRESTWebServices-CodeExample-7

The SelectToken that is used on line 64 accepts a path. This is a search path that points to a certain token that is nested in the JSON data. Find out more about JSONPath here.

The path in the example is ‘$.user.login’. Take a look at the JSON structure and see that this is a nested object.

ALSupportForRESTWebServices-CodeExample-8

That’s it for now, if you have made it to the end of this post: congratulations, you are a assiduous reader!

Of course I plan to update the previously posted web service examples to AL code as well. Some work is ahead!

 

2 thoughts on “AL support for REST Web Services

  1. Pingback: AL support for REST Web Services - Kauffmann @ Dynamics NAV - Dynamics NAV Users - DUG

  2. Pingback: Make your (Dynamics NAV) assistant – Under the Hood

Leave a Reply

Your email address will not be published.