Web Services Examples Part 2 – Verify E-mail Address

3 Dec

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.

The challenge

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 the.cannon.group.plc@cronuscorp.net (who doesn’t recognize this e-mail address?) you would call this URL:

http://api.email-validator.net/api/verify?EmailAddress=the.cannon.group.plc@cronuscorp.net&APIKey=<your API key>

The result is a very short and easy string, a JSON response:

2015-12-02_22-13-40

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.

The code

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?

Deserializing JSON

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:

image.png

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

image.png

This will be your result (a very simple class in this case):

image.png

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.

Download the complete code of this web service example here

Final remarks

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?

13 thoughts on “Web Services Examples Part 2 – Verify E-mail Address

  1. Hi AJK,
    Great example, was there at NavTechDays as well. Im trying to call a service from SharePoint:

    CallRESTWebService(
    //’http://api.email-validator.net/’,
    //STRSUBSTNO(‘api/verify?EmailAddress=%1&APIKey=%2′,’my email’,’1234abc’),
    ‘http://intranet/’,
    STRSUBSTNO(TextURL2,’S008-004′),
    ‘GET’,
    null,
    HttpResponseMessage);

    Where TextURL2 is
    _api/search/query?querytext=’issueid:%1’&selectproperties=’issueid,title,releaseomschrijving’

    But I got an error:
    Een aanroep voor System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode is mislukt met dit bericht: Response status code does not indicate success: 401 (Unauthorized).

    When I follow the exact link in Internet explorer, it does not fail. Do you have any idea?

    • This code is trying to call the REST web service on http://intranet/_api/search/query?–blabla–
      It that what you intend to do?

      The SharePoint api is not anonymous, as far as I know. So the call must carry an authorization header, otherwise it will fail with HTTP status 401.

      I have intentionally left out the authorization part. It is on my list to introduce that in the last part, when calling a NAV web service.

      Hint: use the HttpClient.DefaultRequestHeaders to add authorization to the request.

      • Yes that’s what I intend to do.

        I have it working now, but not with the defaultrequestheaders as you mentioned. (I got an other error when using HttpClient.DefaultRequestHeaders.Add)

        My solution is the following:
        //add these 2 lines
        HttpClientHandler := HttpClientHandler.HttpClientHandler;
        HttpClientHandler.UseDefaultCredentials := TRUE;

        //add httpclienthandler to httpclient:
        HttpClient := HttpClient.HttpClient(HttpClientHandler);

        (HttpClientHandler = System.Net.Http.HttpClientHandler.’System.Net.Http, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a’ )

  2. That’s an option, indeed.

    I was thinking about the NAV process that is running under a certain user account. Now you are using this credentials for the request. With a domain account that would probably work. When using NETWORK SERVICE it becomes more difficult.

    So for that reason I mentioned the Authorization header where you then can specify a specific username / password. It is also possible to set Credentials on the HttpClientHandler as well.

    • True.

      NETWORK SERVICES works also, because I granted access to everyone. Not ideal. So I’m looking forward to an example to run it under a specific user.

      I guess I cannot get the client username/password automatically (because running under Service Tier / other server).

  3. To use a certain username/password/domain use:

    HttpClientHandler.Credentials := NetCredential.NetworkCredential(‘username’, ‘password’, ‘domain’);
    or
    HttpClientHandler.Credentials := NetCredential.NetworkCredential(‘username’, ‘password’);
    where NetCredential= System.Net.NetworkCredential.’System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089′

  4. Pingback: Web Services Examples Part 2 – Verify E-mail Address | Pardaan.com

  5. Pingback: SOAP Web Services konsumieren mit Dynamics NAV - Microsoft Dynamics NAV Community

  6. Pingback: SOAP Web Services konsumieren mit Dynamics NAV - Robert's Dynamics NAV Entwickler Blog - NAV User Group

  7. hello sir..
    “Now you need to copy the JSON result to your clipboard. Use a web browser to get a JSON result that you can use.”
    tell me sir,how to do this?

  8. Hi,

    Thanks a lot for this series of post, this is very helpful !

    I have a “particular” JSON response which has an array in it. It looks like this:
    {
    “firstkey”:”firstvalue”,
    “secondkey”:”secondvalue”,
    “queries”:[
    {
    “position”:0,
    “name”:”MyQuery”,
    “return_type”:”http”,
    “return_url”:”http://myUrl”
    }]
    }

    As you can see, in my JSON object I have an array (queries) which contains objects (here there is only one object)

    My C# code looks like this
    public class Rootobject
    {
    public string firstkey { get; set; }
    public string secondkey { get; set; }
    public int interval_sync_ms { get; set; }
    public bool sync { get; set; }
    public bool auto_update { get; set; }
    public Queries[] queries { get; set; }
    }
    public class queries
    {
    public int position { get; set; }
    public string name { get; set; }
    public string return_type { get; set; }
    public string return_url { get; set; }
    }

    In my C/AL code I do:
    APIGetResponse := JSONConvert.DeserializeObject(JsonObjTxt, GETDOTNETTYPE(APIGetResponse));

    So I can access to the value of firstkey by typing: ApiGetResponse.firstkey;

    But how to access to the queries array ?
    If I do ApiGetResponse.queries[0].return_url;

    It doesn’t work and I don’t have any clue about how to get the content of an array at a specific position.

    Any idea of how to do that ?

    Thanks a lot for the time you spend by reading this ?

  9. Hello, I would like to know how to send headers content to the httpclient like authorization token to access methods in the web service?

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.