Practical .NET

ASP.NET: Configuring WCF for REST with JSON

If you want to extend your ASP.NET application to include RESTful calls that return JSON results to JavaScript code in an AJAX page, here's how to manage your URLs to reduce errors, integrate with ASP.NET MVC, and pass objects from your browser to your server.

In this month's article in the print edition of Visual Studio magazine (WCF and Service Oriented Architectures), I looked at the major components of a Service Oriented Architecture (SOA) and showed how to use Visual Studio 2010 and WCF 4 to implement those components. To fit everything into the space available, I just gave the highlights for integrating AJAX-with-REST-and-JSON services into an ASP.NET application. This column fills in the details.

Quick Review
In the article I described how to convert an existing ASP.NET application to use REST-with-JSON. You add these tags to your web.config file

<system.serviceModel>
  <standardEndpoints>
    <webHttpEndpoint>
      <standardEndpoint name="" helpEnabled="true"
                    automaticFormatSelectionEnabled="true"/>
    </webHttpEndpoint>
  </standardEndpoints>
  <serviceHostingEnvironment aspNetCompatibilityEnabled="true"
 multipleSiteBindingsEnabled="true" />
</system.serviceModel>

And these tags inside the system.webserver element:

  <modules runAllManagedModulesForAllRequests="true">
    <add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, 
               System.Web, Version=4.0.0.0, Culture=neutral,
               PublicKeyToken=b03f5f7f11d50a3a" />
  </modules>

You also add references to the System.ServiceModel.Web and System.ServiceModel.Activation libraries. Finally, you add a class file that includes the methods you want to be able to call from your JavaScript code. That class needs these attributes:

Imports System.ServiceModel
Imports System.ServiceModel.Web
Imports System.ServiceModel.Activation

<ServiceContract()>
<Activation.AspNetCompatibilityRequirements(
    RequirementsMode:=AspNetCompatibilityRequirementsMode.Allowed)>
<ServiceBehavior(InstanceContextMode:=InstanceContextMode.PerCall)>
Public Class MyRESTService

In the example I used for the article, I had a method that accepts a single string as a parameter and returns a Customer object. Like any of the methods you want to call, it needs the WebGet attribute:

<WebGet()>
Public Function GetCustomer(cId As String) As Customer

To map a URL to your method you need to add a Routing rule in the site's Global.asax file. This code ties the class MyRESTService to any URL for the site that begins with "TheService":

RouteTable.Routes.Add(New ServiceRoute("TheService",
  New WebServiceHostFactory(), GetType(MyRESTService)))

Assuming you've done all this work, you should be able to test that GetCustomer method from JavaScript in an ASPX page using jQuery's getJSON method. In this example, to pass the cId parameter to the GetCustomer method, I put a name/value pair in the service's URL :

<script src="Scripts/jquery-1.4.1.js" type="text/javascript"></script>
<script type="text/javascript">
$(function () 
  {
   $.getJSON(
      "http://localhost:1867/TheService/GetCustomer?cId=A123",
      function (data) 
      {
       $("#result").text(data.FirstName); 
       $("#result").show(); 
      }
     );
  }
);
</script>

Enhancing the Service's URL
Putting parameters in the querystring makes it obvious what values are going to which parameters on the method. However, there are some downsides. The names used in the querystring must match the names of the parameters in the method, for instance. If you get the name of the parameter wrong in the querystring then the parameter on the method is passed nothing. And querystrings do make for longer URLs

You can bypass both of these problems by integrating your parameters into the URL used in your request. A URL that incorporates the cId could look like this, for instance:

http://localhost:1867/TheService/GetCustomer/A123

To specify that the final part of this URL is to be flagged as the CId parameter, you use the UriTemplate property on the WebGet method of your method. In this case, that would look like this:

<WebGet(UriTemplate:="GetCustomer/{cId}")>
    Public Function GetCustomer(cId As String) As Channels.Message

This feature also lets you use a different name in the URL than the name of the method. To use a URL that references "CustomerById" rather than "GetCustomer", you'd use this WebGet attribute on the GetCustomer method:

<WebGet(UriTemplate:="CustomerById/{CustomerId}")>
    Public Function GetCustomer(CustomerId As String) As Channels.Message

To create a "default" method that just requires parameters in its URL, you can omit the method name from the UriTemplate:

<WebGet(UriTemplate:="{CustomerId}")>
    Public Function GetCustomer(CustomerId As String) As Channels.Message

This method would be called with this URL:

http://localhost:1867/TheService/A123

While eliminating a source of error (and shortening the URLs required to access a method on a service) you do have to remember the order in which the parameter's must appear in the URL. And replacing method names in the URL (or omitting them) may be going into the realm of job security programming.

Passing More Data
Methods decorated with WebGet can only be accessed using an HTTP GET (e.g. with a bare URL, as I've been doing so far). However, as I said, plugging a lot of data into the URL has its issues -- not to mention the pain of creating a separate parameter on your method for each name/value pair in the querystring.

If you decorate your method with the WebInvoke attribute, you can send back whole objects in a separate post rather than in your service's URL. For instance, if you want to update a Customer object, it would be much easier to send back a single Customer object holding the values you want to update than all the individual properties as name/value pairs in the querystring.

To Post data, you need to create an object in JSON format holding the data to go to the server. In this example, I've set the values for two of the Customer object's properties (the primary key and the property to be updated):

var cust = '{"CustomerId":"A123", "LastName":"Irvine"}';

In theory, I should be able to use the $.post shortcut to call my WCF method but, in practice, I've found it's as easy to use the longer $.ajax call (which also lets me specify a separate routine to handle errors). This example sends my data to an UpdateCustomer method on my WCF and, on return, calls a function to shove the result into elements with their id attributes set either to "result" or "error":

$.ajax({
        type: "POST",
        contentType: "application/json",
        url: "http://localhost:1867/TheService/UpdateCustomer",
        dataType: "json",
        data: cust,
        success: function (data) {
                    $("#result").text(data);
                    $("#result").show();
                },
        error: function (data) {
                    $("#error").text(data);
                    $("#error").show();
                }
        }
    );

The matching WCF service method looks surprisingly normal except for the <WebInvoke> attribute. This code tests to see if the properties in the Customer object passed to the method contain any values and performs an update if a value is found:

<WebInvoke()>
Public Function UpdateCustomer(custIn As Customer) As String
 Try
   Dim cust As New Customer(custIn.CustomerId)
   If custIn.FirstName IsNot Nothing Then
       cust.FirstName = custIn.FirstName
   End If

   If custIn.LastName IsNot Nothing Then
       cust.LastName = custIn.LastName
   End If
   Return "Success"
 Catch
   Return "Failure"
 End Try

End Function

I can get away with checking these properties for Nothing because a string is a reference type. If my properties were value types (e.g. Integers), I'd either need to mark them as nullable (e.g. using Integer?) or create a data transfer object for communicating with my client that passed all values as string.

One final note: if using two attributes is too complicated, you can specify GET as the HTTP method for the WebInvoke attribute using the WebInvoke's Method property. That way, you can just use WebInvoke all the time because these two attributes are identical:

<WebInvoke(Method:="GET")>
<WebGet()>

You can't get much simpler than that!

Sidebar: Integrating with ASP.NET MVC

If you've added your WCF service to an ASP.NET MVC site, then you'll want to make sure that the route for your WCF service doesn't step on the toes of any of your other routes. The first step is to add this route after all other routes -- this ensures that your ActionLink helper will build the routes to your controllers correctly. The second step is to add a constraint to any of your routes that don't specify an explicit controller name (i.e. routes that use {controller}) to ensure that MVC ignores routes beginning with your prefix. This ensures that MVC doesn't whine about the fact that you don't have a controller with the same name as your service's prefix.

Since my prefix is "TheService", a regex expression like "^(?!TheService).*" on routes that include {controller} ensures that MVC doesn't complain that I don't have a controller called "TheService".

About the Author

Peter Vogel is a system architect and principal in PH&V Information Services. PH&V provides full-stack consulting from UX design through object modeling to database design. Peter tweets about his VSM columns with the hashtag #vogelarticles. His blog posts on user experience design can be found at http://blog.learningtree.com/tag/ui/.

comments powered by Disqus

Featured

Subscribe on YouTube