This is the second part in a series about useful web services. The series was introduced with this post. In the first part I have introduced the basic pattern for calling any REST web service from Dynamics NAV. Now with this post I want to jump into the first example that was demonstrated at Directions EMEA, Directions US and NAV Techdays 2015.
Let’s first talk about the problem (or challenge if you want) that we want to solve. You might think that a web service for checking an e-mail address is somewhat overkill. There are plenty of examples how to check if an e-mail address is valid. A common example is to use a regular expression to check if the e-mail address has a valid format.
But does a valid format guarantee that the e-mail address is valid? I mean, valid in the way that there is really an e-mail server that accepts a sent message to this e-mail address?
Aha, now I see you thinking…
No, it is not possible to check if an e-mail address is really existing without reaching out to the mail server that hosts the account. But is it really possible to verify it? The answer is yes, there are a bunch of web services out there to perform this task. No need to reinvent the wheel, let other parties do that part for you.
How did I find such a service? Well, just by searching it using keywords like “web service validate email address” and then start browsing and exploring the search results.
The web service
For my example, I picked http://www.email-validator.net/ as the preferred web service. They have a simple API, low costs and the web services has a fast response time. They do not only test the e-mail address for availability, there is also a check if it is a so-called disposable address, no-reply address, etc.
First of all you need to grab a trial key here: http://www.email-validator.net/email-validation-online-api-get-free-trial.html. This trial key can be used for up to 100 addresses.
How does it work?
The REST web service is called by a single URL containing a couple of parameters. All details can be found here: http://www.email-validator.net/email-address-online-verification-api.html
For example, to test the e-mail address firstname.lastname@example.org (who doesn’t recognize this e-mail address?) you would call this URL:
http://api.email-validator.net/api/verify?EmailAddressemail@example.com&APIKey=<your API key>
The result is a very short and easy string, a JSON response:
You can just try this out in your browser.
Look at the JSON response. It contains three properties: status, info and details. We want to get this information out of the response so we know if the e-mail address was indeed valid.
Ok, let’s look how to get this to work in Dynamics NAV.
In this code example we use the function CallRESTWebService that was introduced in Part 1: The Basic Pattern.
RESTWSManagement.CallRESTWebService( 'http://api.email-validator.net/', STRSUBSTNO('api/verify?EmailAddress=%1&APIKey=%2',Email,APIKey), 'GET', null, HttpResponseMessage); result := HttpResponseMessage.Content.ReadAsStringAsync.Result;
Let’s look at the parameters of the CallRESTWebService:
BaseUrl = the base address of the web service: http://api.email-validator.net/
Method = the web service method: api/verify?EmailAddress=<email>&APIKey=<key>
RestMethod = the REST verb to use, in this case just a GET
HttpContent = null, since we don’t use POST or PUT
HttpResponseMessage = the response we get back from the web service
The result is just a string and can be read from the response message by calling the Content.ReadAsStringAsync method. This is a asynchronous method, and because Dynamics NAV doesn’t support that we use the .Result so the code waits for the method to be finished.
Now we have the JSON text in our variable ‘result’ which is a regular Dynamics NAV text type.
How to get the properties out of this variable?
There are several options to get the properties out of a JSON string. Of course you can look for a generic way to achieve this. And to a certain extent that would work.
At the other hand, wouldn’t it be more easy if you could do something like APIResult.Status instead of dealing with texts and complex generic functions? In .Net terms we call that strong typing.
Well, it turns out that Visual Studio can be of help for that. You can use Visual Studio to create a class that can be used to build an object, filled with the data from the JSON string. This is called deserializing.
Let’s first look how this works in Visual Studio. Create a new project of type Class Library. Let’s say we name the project EmailValidatorResult.
in Class1.cs remove the code, except the usings:
Now you need to copy the JSON result to your clipboard. Use a web browser to get a JSON result that you can use.
Now use Edit –> Paste Special –> Paste JSON as Classes
This will be your result (a very simple class in this case):
Now you can compile this project to a dll file (in .Net terms an assembly) and deploy it to the Add-ins folder of the Dynamics NAV server. Create a DotNet variable of type EmailValidatorResult.RootObject from this assembly.
The next step is to create this object and fill it with the data from the JSON response. For this we can use the Newtonsoft.Json.dll, an assembly that provides convertors and other very useful stuff to handle JSON data. You can read more about this tool on their website: http://www.newtonsoft.com/json
It is not needed to download the dll, it is already available in the Dynamics NAV server folder. The only thing you need to do is to copy the Newtonsoft.Json.dll to the Add-ins folder of the server. And the good news is that you only need to do this during development time. It enables you to pick up the assembly from the variables dialog in C/SIDE. During runtime it will automatically load the assembly from the server folder, so no need to deploy this file to the Add-ins folder in a production environment.
We are almost there, only two steps left. Create a DotNet variable of type Newtonsoft.Json.JsonConvert from the Newtonsoft assembly.
Finally you can write code like this:
APIResult := JsonConvert.DeserializeObject(result,GETDOTNETTYPE(APIResult)); MESSAGE(APIResult.Status);
Let’s put it all together in a complete function:
ValidateEmailAddress(Email : Text) Window.OPEN('Verifying E-mail Address...'); RESTWSManagement.CallRESTWebService( 'http://api.email-validator.net/', STRSUBSTNO('api/verify?EmailAddress=%1&APIKey=%2',Email,APIKey), 'GET', null, HttpResponseMessage); result := HttpResponseMessage.Content.ReadAsStringAsync.Result; APIResult := JsonConvert.DeserializeObject(result,GETDOTNETTYPE(APIResult)); Window.CLOSE(); IF NOT (APIResult.status IN [200,207,215]) THEN BEGIN messageText := 'Verifying E-mail Address...' + Environment.NewLine + Environment.NewLine; messageText += 'Info: ' + APIResult.info + Environment.NewLine + Environment.NewLine; messageText += 'Details:' + Environment.NewLine; separator := '.'; details := APIResult.details; detailsArray := details.Split(separator.ToCharArray()); FOREACH detail IN detailsArray DO BEGIN messageText += detail + Environment.NewLine; END; MESSAGE(messageText); END;
As you can see, this code also contains an example how to split the details part into separate lines that can be displayed in a message. For an explanation of how to split a string into an array, look at my previous post.
Make sure that you have a complete JSON example when you create a class using Visual Studio. Or modify the class if you know that certain values can be different. One problem that I stumbled over was a JSON example that contained an integer. Visual Studio created that nicely, but the other day the call failed. It turned out that the integer value in the JSON response was now a decimal.
Why check if the APIResult.Status is 200, 207 or 215? According to the documentation of the web service, these codes are used for a valid e-mail address. See http://www.email-validator.net/email-verification-results.html.
Do you really need to split the url in two parts, a base url and a method part? The answer is no, that’s not mandatory. However, it has something to do with the design of the HttpClient object. This object is designed to be used frequently. Instantiate it once, assign a base url to it, and then call a number of methods on it. With a single instance Codeunit in NAV we could achieve the same behavior. But that’s for the advanced stuff, maybe later…
For the close readers: you may have seen that I use a null variable in the code. Is that something new in NAV 2016? No, it isn’t… It is just a System.Object type that has not been instantiated. So the actual value is null. Got it?