Android Ax App – Part 1–Set up the intermediate WCF service


I came across a blog by Joris De Gruyter http://daxmusings.blogspot.com/2011/11/10-minute-app-windows-phone-7-app.html and decided to use that to connect to an Android device.

This is part 1 of 2 to explain the process of creating the Android application to connect to Ax via AIF services.

Unlike the blog mentioned above, I had to enable REST in the WCF service, and output it as JSON. This is because android has a better JSON support than SOAP (ksoap can be used to consume the WCF services, but I have found JSON to be a much easier approach)

To enable REST services, I installed the WCFRestContrib package. This is available from NuGet and can be installed using

PM> Install-Package wcfrestcontrib

We can create the WCF Service using the no configuration mode. This link defines how to create a WCF service with no configuration. Which means most of the web.config is deleted, and another identifier is attached to the SVC file. This however relies of transport security as there is no SOAP envelope around this. WCFRESTContrib allows to create a custom authentication system. The authentication data can be retrieved from the HTML headers and verified.

So the WCF service should look like this, it enables a GET method and has a certain URI, and outputs the data in JSON

namespace WcfService1
{
  [ServiceContract]
  [ServiceAuthentication] //WCFRESTContrib attribute - authentication per session (or at method level for per call) public interface IApi1 {
  [WebGet(UriTemplate = "/api1/appName", ResponseFormat = WebMessageFormat.Json)]
  [OperationContract]
  string ApplicationName();

  [WebGet(UriTemplate = "/api1/items", ResponseFormat = WebMessageFormat.Json)]
  [OperationContract]
  List GetAllItemIds();
}
}

The Service interface markup (this allows for a noconfiguration WCF Service)

<%@ ServiceHost 
    Language="C#" 
    Debug="true" 
    Service="WcfService1.Api1" 
    CodeBehind="Api1.svc.cs"
    Factory="System.ServiceModel.Activation.WebServiceHostFactory"
    %>

The Service file itself

namespace WcfService1
{
    [WebAuthenticationConfiguration(
    typeof(WcfService1.ApiAuthnetication),
    typeof(ApiAuthneticationValidator),
    true,
    "My Application")]
    public class Api1 : IApi1 {
    public string GetUsername()
    {
        object authValue;
        string authStr = "", username = "", password = "";
        try {
            var keys = OperationContext.Current.IncomingMessageProperties.Keys;
            OperationContext.Current.IncomingMessageProperties.TryGetValue("httpRequest", out authValue);
            if (authValue != null)
            {
                System.ServiceModel.Channels.HttpRequestMessageProperty p = (System.ServiceModel.Channels.HttpRequestMessageProperty)authValue;
                authStr = p.Headers.Get("Authorization");
                ApiAuthnetication.GetUsernamePwd(authStr, out username, out password);
            }
        }
        catch (Exception e)
        {
            return e.Message;
        }
        return username;
    }
    public string ApplicationName()
    {
        string username = this.GetUsername();

        return username;
    }
    public List GetAllItemIds()
    {
        return Item.GetItems(this.GetUsername());
    }
    }
}

Notice the header attributes. This needs to be defined and is based on the WCFRESTContrib package. The authentication classes created is. Notice that the Username and password are being sent in the header with the tag “Authorization” this can be renamed to anything you like

namespace WcfService1
{
    public class ApiAuthnetication : WcfRestContrib.ServiceModel.Dispatcher.IWebAuthenticationHandler {
    public static bool GetUsernamePwd(string authenticationStr, out string username, out string password)
    {
        bool result = true;
        username = "";
        password = "";
        try {
            var values = authenticationStr.Split(':');
            username = values[0];
            password = values[1];
        }
        catch {
            result = false;
        }
        return result;
    }

    public System.Security.Principal.IIdentity Authenticate(
            System.ServiceModel.Web.IncomingWebRequestContext request,
            System.ServiceModel.Web.OutgoingWebResponseContext response,
            object[] parameters,
            System.IdentityModel.Selectors.UserNamePasswordValidator validator,
            bool secure,
            bool requiresTransportLayerSecurity,
            string source)
   {
       string authHeader = "";
       string userName = "";
       string password = "";
       if (request.Headers.HasKeys() == true && request.Headers.AllKeys.Contains("Authorization") == true)
       {
           authHeader = request.Headers.Get("Authorization");
           //var values = authHeader.Split(':');
           //userName = values[0];
           //password = values[1];
           GetUsernamePwd(authHeader, out userName, out password);
       }
       validator.Validate(userName, password);
       return new GenericIdentity(userName, this.GetType().Name);
   }
}

   public class ApiAuthneticationValidator : System.IdentityModel.Selectors.UserNamePasswordValidator
   {
       public override void Validate(string userName, string password)
       {
          //Do your custom authentication here
          if (userName.Equals("claimguy@myapp.com", StringComparison.InvariantCultureIgnoreCase) == false
                  || password.Equals("claimguy@myapp.com", StringComparison.InvariantCultureIgnoreCase) == false)
              throw new Exception("Authentication Failed");
       }
   }
}

The code to get the InventTable data is pretty similar to Joris’s blog. Here is mine:

namespace WcfService1
{
    public class Item {
        public string ItemId { get; set; }
        public string Name { get; set; }

        public static List GetItems(string username)
        {
            List items = new List();
            string itemIdFrom = "1000"; //I cheated here 
            string itemIdTo = "1105";   //I cheated here again 
            //Fetch items from AX here 
            using (ItemService.ItemServiceClient client = new ItemService.ItemServiceClient())
            {
                ItemService.QueryCriteria qc = new ItemService.QueryCriteria();
                ItemService.CriteriaElement criteria = new ItemService.CriteriaElement();
                criteria.DataSourceName = "InventTable";
                criteria.FieldName = "ItemId";
                criteria.Operator = ItemService.Operator.Range;
                criteria.Value1 = itemIdFrom;
                criteria.Value2 = itemIdTo;
                qc.CriteriaElement = new ItemService.CriteriaElement[] { criteria };

                ItemService.CallContext context = new ItemService.CallContext();
                context.Company = "ceu";
                context.MessageId = Guid.NewGuid().ToString();
                context.LogonAsUser = string.Format("LoonAx\\{0}", username);

                var axItems = client.find(context, qc);
                if (axItems.InventTable != null)
                {
                    foreach (var inventTable in axItems.InventTable)
                    {
                        Item item = new Item() { ItemId = inventTable.ItemId, Name = inventTable.NameAlias };
                        items.Add(item);
                    }
                }
            }
            /* //Mocking for AIF
            for (int i = 0; i < 10; i++) { Item item = new Item() { ItemId = (1000 + i + 1).ToString(), Name = (1000 + i +          1).ToString() + " - name" }; items.Add(item); }*/ return items;
        }
    }
}

To make the connection secure (since we are sending the authentications via plain text in the HTML headers, this service should ideally be secure)

In the next blog I will go through the Android app to consume this service

Advertisements

17 thoughts on “Android Ax App – Part 1–Set up the intermediate WCF service

  1. Hey you speak of enabling REST Services using WCFRestContrib, If I get this right, this should be installed in the MS AX server. Still, I don\’t see where I should put :

    PM> Install-Package wcfrestcontrib

    Can I install it without using that command? If not where should I input it?

    Thanks in advance!

    Like

    1. Hi Magnus,
      WCFRestContrib is now discontinued, and has been replaced by Web API. this is now a standard part of .Net 4. A fair but has changed in the implementation, but the calls to the server remain the same

      Like

    2. Hi Magnus,
      WCFRestContrib is now discontinued, and has been replaced by Web API. this is now a standard part of .Net 4. A fair but has changed in the implementation, but the calls to the server remain the same

      The Install-Package command is run the the Package manager console within Visual studio for that project. Its a command line for nuget

      Like

  2. Thanks a lot for the quick answer!! I am trying to follow your steps and connect an android phone to Dynamics 2012 in order to recover user Alerts. (I have yet to find an other tutorial as complete as yours to do so). I guess I should look around for how to connect to web API from Android phones then.

    Also, If I understood well what you did, you did not use a trusted intermediary like in DaxMusings windows app post, since you made your own “lweb authentification”. Will I have to make my own authentification with the web API?

    Anyways thanks again for the answer and if you know of a website that could help me I would greatly appreciate if you could point me to it!

    Like

    1. HI Magnus,
      What i did use was a BasicAuthentication. If you search for it there are a few implementations on how to apply it to your Server project.
      I did use the intermediate authentication like Joris’s example. I passed that value in the Basic Authentication of the REST call.
      Since WCFRESTContrib there has been 2 releases (last one being the WEB API) and i had to change those authentication procedures for each of them.
      I shall find an example and reply to you

      Like

  3. Oh Oki, I’ll try to work with that then! Thanks for the link!

    Also I wondered, at one point you say: “Put your custom authentication Here”. Since my aim is to get the Alerts associated to one user and not all of them, I can implement an authentication that recovers user and pwd values from a custom Table? (Table could be filled by a user with a form where he “allows” internet access to certain users).

    Maybe there are much easier ways, i’m just a beginner in AX dev. Sorry to bug you with so many questions :s

    Like

    1. Hi Magnus,
      You may want to email me to discuss more regarding this. (look at my contact info for that, or twitter)
      The place where I say “Custom authentication” is to tell you that if you are using the trusted intermediary, then Ax does not store any authentication information for that user. So you may either just allow anyone or have your own table to do the authentication, which will probably sit outside Ax.

      Like

    1. Hi Louie, This blog is not aimed at how to create a WCF Service. There are other tutorials for this. At this stage, if you are still planning to create a WCF Service, you may want to look at using Web API instead.

      Like

Leave a Reply / Comment

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s