Sunday, June 29, 2014

Proxy Class For Consuming RESTful Services

About 2 years ago, I wrote a blog post about using generics to create an easy-to-use WCF proxy class (click here: A Generic WCF Proxy Class).

A few months ago, I had the need to consume several RESTful web services that made use of JSON. Wanting to make is as easy to use as ProxyBase, the generic WCF proxy class that I posted 2 years ago, I decided to write another base class for RESTful web service access. I call it ProxyBaseREST.

There are several things you will need to add to your project references that aren't normally added when you create a new class library project (and also remember to put using statements for them at the top of your class). One is System.Net, because we'll need to make use of the WebClient, which lives in that namespace. Since we'll also be dealing with JSON, you need a JSON library. I've made use of Json.NET (the assembly is called NewtonSoft.Json.DLL), which is widely used. It's free open-source and you can download it here: https://json.codeplex.com/ And, two more, System.Xml and System.Xml.Linq, since we'll be converting JSON to either XNode or XmlNode.

So, here's the class I wrote:

public class ProxyBaseREST
{
    #region Declarations and Constructor

    protected string BaseAddress;
    protected string Parameters = "";

    public ProxyBaseREST(string baseAddress)
    {
        this.BaseAddress = baseAddress;
    }

    #endregion

    #region Methods

    protected string GetJSON()
    {
        try
        {
            WebClient WC = new WebClient();
            WC .Headers["Content-type" ] = "application/json";
            //string CallString = this.ParseParameters();

            Stream stream = WC.OpenRead( this.BaseAddress + this .Parameters);

            StreamReader reader = new StreamReader(stream);
            string json = reader.ReadToEnd();
            stream .Close();

            if (json == "[]")
                json = "" ;
            return json;
        }
        catch (Exception ex)
        {
            Console.WriteLine("Error in Web Service call: {0}{1}" , this.BaseAddress, this.Parameters);
            Console.WriteLine(ex.Message);
            return "" ;
        }
    }
    protected string GetDeserializedJSON(string rootNode)
    {
        string json = this.GetJSON();
        string xml = "";
        try
        {
            xml = this .DeserializeAsXNode(json, rootNode);
        }
        catch (Exception ex)
        {
            Console.WriteLine("Error Deserializing JSON RootNode '{0}' in Web Service call: {1}{2}", rootNode, this.BaseAddress, this.Parameters);
            Console.WriteLine(ex.Message);
            xml = "" ;
        }

        return xml;
    }
    protected string DeserializeAsXNode(string json, string rootNode)
    {
        if (string.IsNullOrEmpty(json)== false)
        {
            XNode node = JsonConvert.DeserializeXNode(json, rootNode);
            return node.ToString();
        }
        else
            return "" ;
    }
    /// <summary>
    /// Used for mal-formed JSON that lacks a root node
    /// </summary>
    /// <param name="json"></param>
    /// <param name="rootNode"></param>
    /// <returns></returns>
    protected string GetDeserializedJSON(string json, string rootNode)
    {
        string xml = "";
        try
        {
            if (string.IsNullOrEmpty(json) == false)
            {
                string RootAndJson = "{\"" + rootNode + "\":" + json + "}";
                System .Xml.XmlNode myXmlNode = JsonConvert.DeserializeXmlNode(RootAndJson, rootNode);
                xml = myXmlNode.OuterXml;
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("Error Deserializing JSON RootNode '{0}' in Web Service call: {1}{2}", rootNode, this.BaseAddress, this.Parameters);
            Console.WriteLine(ex.Message);
            xml = "" ;
        }

        return xml;
    }

    #endregion
}

You'll note that the ProxyBaseREST class makes use of a couple of JsonConvert static methods to convert the JSON format to an XML string. You can change this if you'd like to return XNode or XmlNode, but I prefer strings because I typically use XML to fill a DataSet, which is easily done with an XML string. You can obviously change the methods to return whatever kind of XML you would normally use in your applications.

Now all you have to do is create a sub-class of ProxyBaseREST, add the methods you need to call and you're done. Here's a sample Proxy:

public class MyProxy : ProxyBaseREST
{
    #region Declarations and Constructor

    // constructor
    public MyProxy(string baseAddress) : base(baseAddress)
    {
    }

    #endregion

    #region Methods

    public string GetDataNoParms()
    {
        this.Parameters = "GetData";
        string xml;

        // use one or the other of these two methods
        // comment out (or remove) the one you don't want, obviously

        // this returns a string that is formatted into XML
        xml = this.GetDeserializedJSON("root"); // this "root node" can be called anything

        // this returns a string in the JSON format, just as you've received it from the web service call
        xml = this.GetJSON();

        return xml;
    }
    public string GetDataWithParms(string CustomerName, int CustomerID, bool PreferredCustomer)
    {
        string parms = string.Format("CustomerName={0} CustomerID={1} PreferredCustomer={2}", CustomerName, CustomerID, PreferredCustomer);
        this.Parameters = "GetData?params=" + parms;
        string xml;

        // same two choices as above
        xml = this.GetDeserializedJSON("root");
        xml = this.GetJSON();

        // And here's another situation. You might have mal-formed JSON returned from the web service. 
        // This is fine for just returning the JSON, maybe. But, if you need to serialize it
        // to XML, you'll need to do something extra.
        // The ProxyBaseREST class has a method for that as well, use it in conjunction with the GetJSON() method:
        xml = this.GetDeserializedJSON(this.GetJSON(), "root"); // this "root node" can be called anything

        return xml;
    }

    #endregion
}

And then, you'd make web service calls like this:

public class TestingMyProxy
{
    #region Declarations

    // In real-life, you'd get the address from the appSettings in your config file.
    protected string WebServiceAddress = @"http://192.168.1.161:7777/"; 
    protected MyProxy myProxy;

    #endregion

    #region Constructor

    public TestingMyProxy()
    {
        this.myProxy = new MyProxy(this.WebServiceAddress);
    }

    #endregion

    #region Call Proxy Methods

    public void TestProxyMethods()
    {
        Console.WriteLine("---  GetDataNoParms  ---");
        Console.WriteLine(this.myProxy.GetDataNoParms());
        Console.WriteLine("--- GetDataWithParms ---");
        Console.WriteLine(this.myProxy.GetDataWithParms("CustomerBob", 12345, true));
    }

    #endregion
}

5 comments:

  1. This comment has been removed by a blog administrator.

    ReplyDelete
    Replies
    1. I'd sure like to know if you're legitimate or not, I get so many spam comments on my blog. A real name other than Anonymous would be nice. And maybe more about you on LinkedIn or something similar. I hesitate to click on your link .... I don't want viruses, etc. on my computer!

      Delete
  2. I would suggest you delete the anon link.

    ReplyDelete
    Replies
    1. If you're talking about the link to json.codeplex.com, it's still a legitimate link for downloads. If that's not what you're talking about, then could you elaborate?

      Delete
    2. Yeah, I'm an idiot ... and then I forgot all about these comments from almost a year ago. Revisiting this post today, I saw that that other Anonymous comment that had a link was still there, so I deleted it.

      Delete