Demystifying The Code

REST in WCF – Part VI (HI-REST – Consuming our GET service via AJAX)

In Part V of this blog series I completed the service operation exposed via HTTP GET in a HI-REST manner.  Because the service was exposed via GET and had a representation format of POX, we were able to easily test it using our browser.  That is all well and good for testing purposes, but we are going to need to call our service from a real client, at some point.  We have a variety of choices when deciding upon our clients… We could write an ASP.NET client, an AJAX client, a Silverlight client, a smart client, an office client, etc.  For the purposes of this blog post, I will illustrate how you can call this service from an AJAX client.  By the time I am done with this post, you will have a newfound respect for the client proxy generation functionality exposed by the enableWebScript endpoint behavior (described in Part III of this series).

 

The AJAX-Friendly Client Proxy Revisited

There is a famous acronym in software development circles called CASE.  It stands for Copy Always Steal Everything.  Now, certainly I don’t advocate stealing.  I bring up the saying because it reminds me not to re-invent software.  If someone else has written it and tested it and I have a legal right to use it, why should I bother re-implementing?  In that light, I am going to try to take advantage of the ASP.NET AJAX Client Libraries as much as possible.  In Part III of this series I illustrated that the enableWebScript endpoint behavior generated a client proxy ASP.NET AJAX class that allowed us to easily call our service.  If you examine the proxy, you will note that the ASP.NET AJAX class it creates subclasses WebServiceProxy and the implementation of the method simply calls _invoke which is implemented in Sys.Net.WebServiceProxy in the ASP.NET AJAX Client Libraries.  See below:

image

 

Finding the ASP.NET AJAX Client Libraries

Well, I know that under the hood, the proxy that was generated takes advantage of the ASP.NET AJAX Client Libraries and I am going to do the same.  The only question is how.  First of all, the question that typically arises is: ‘Where in the heck are the client libraries anyway?’.  If you look below, you will see that the client libraries are compiled as embedded resources in System.Web.Extensions.dll (btw I am using reflector to interrogate the System.Web.Extensions.dll).

image

I would suggest copying the code from this library into a text file and name it MicrosoftAjax.debug.js and store it somewhere on your file system, as you will be referencing it over and over again as time goes on.  You can either copy the code from the disassembler pane in Reflector or if you are debugging in Visual Studio, you can click in the Script Documents section on the ScriptResource.axd handler reference (see below).

image

 

"Borrowing" From the Client Libraries?

The first thing I looked for in the client libraries is the _invoke method.  I wanted to see if I could simply call into that.  Looking at that method below, I noticed that after doing some validation (which I omitted), the call was simply forwarded to an invoke method.

image

Taking a look at the meat of the invoke method (shown below), you will see that most of the real work is eventually done by the Sys.Net.WebRequest object.  In invoke, we are mainly creating an instance of WebRequest, setting some properties and calling invoke on that instance.  I underlined the calls that are of particular importance.

image

I also pointed out a problem we would incur if we simply tried to call _invoke directly.  If you were to debug through the _createUrl function, you would see that this method appends the methodName passed in as the final url segment and appends the urlParameters as querystring parameters.  For our GET service, the url resulting from this method would look like this:

/ClientStore/RESTCatalogService.svc/GetProduct?productName=Cheerios

 

Creating restInvoke

This would be fine if we set up our UriTemplate to match this (UriTemplate="GetProduct?productName={productName}").  However, this seems to fly in the face of our goal which is a nice hackable Uri for our resources.  For that reason, I decided that I was not simply going to call _invoke like the proxy did.  There is another major issue.  If you take a look at the signature of _invoke, you will notice a ‘useGet’ argument which is a boolean.  If this is false, the HTTP VERB is assumed to be POST.  We discussed earlier that our goal is to use the appropriate HTTP verb for the appropriate action.  So we are going to need to be able to use PUT, DELETE and perhaps some others.  What I decided to do was to create my own function in System.Net.WebServiceProxy called restInvoke (probably a crappy name, but I did it in a hurry and I now have code out there referencing it — let that be a lesson to you).  In JavaScript, you cannot seal a class – er – object, as the case may be.  You can simply add your own functions to an existing object and that is exactly what I did.  I copied most of the implementation from Sys.Net.WebServiceProxy.invoke, but allowing for the use of differing http verbs.  It is now the job of the caller to pass in the full URL.  Here is a subset of what I came up with (You can find the entire script in the Starter Solution):

image 

 

Consuming our new restInvoke

We are finally ready to call our HI-REST WCF service operation.  I have a simple management page that will eventually allow me to add/insert/update and fetch products.  After I am done here, we will have the fetch implemented.  The resulting page looks like this:

image

When the ‘Get Product’ button is pressed, we will create a url that will match our UriTemplate, complete with the productName parameter.  Here is the code in the event hander:

function getProduct_clicked() {

    var product = $get('productList').value;
    var url = "../RESTCatalogService.svc/product/" + product;

    var proxy = new Sys.Net.WebServiceProxy();
    proxy.restInvoke(url,
         "GET",
         null,
         "getProduct_clicked",
         ProductLoadedEventHandler,
         ErrorEventHandler,
         null);
}

function ProductLoadedEventHandler(result) {
    var productName = "";
    var description = "";
    var price = "";
    var productImage = "";

    // Set the vars if the response comes back 
    if (result != null) {
        productName = result.ProductName;
        description = result.Description;
        price = result.Price;
        productImage = result.ProductImage;
    }

    // Set the textboxes 
    $get('productName').value = productName;
    $get('description').value = description;
    $get('price').value = price;
    $get('image').value = productImage;
}

As you can see, we simply create a relative url, matching the UriTemplate.  We create an instance of the WebServiceProxy and call the restInvoke function we added to that object.  The callback method simply sets the value of each of the textboxes.  If you are not familiar with ‘$get’ it is ASP.NET AJAX cross-browser compatible version of getElementById.  In order to make this work, you will have to include the script file containing your new restInvoke method on the page (just add a script reference to the ScriptManager):

<Scripts>
    <asp:ScriptReference Path="~/script/Sys.Net.WebServiceProxy.restInvoke.js" />
</Scripts>

and make sure that you set your ResponseFormat to Json:

[OperationContract]
[WebGet(UriTemplate = "product/{productName}",
    ResponseFormat = WebMessageFormat.Json)]
ProductData GetProduct(string productName);

That should do it.  I am now able to call my HI-REST GET from an AJAX Client, as well as call any other HI-REST method which we will see in later posts.

Until then… Enjoy!

Speak Your Mind

Tell us what you're thinking...
and oh, if you want a pic to show with your comment, go get a gravatar!

Demystifying The Code