REST in WCF – Part V (HI-REST – Exposing a service via GET – The ServiceContract and Implementation)

(Warning… I made up my own words and was not afraid to use them in this post… and others)

In yesterdays post (Part IV of the series), I began the process of developing a HI-REST GET service operation. I noted that there is not a template (yet – it is coming in the REST toolkit due out later this year) for HI-REST services. So, I decided to start by using the plain vanilla ‘WCF Service’ Visual Studio template. This template defaults to a HTTP SOAP binding that supports advanced web service standards. I next illustrated what we need to do to convert this configuration to support a HI-REST service. Essentially it boiled down to 3 things: 1. Set the binding in your endpoint to webHttpBinding, 2. Add a behavior under the endpointBehaviors element that has a webHttp child element and 3. Add a behaviorconfiguration to your endpoint that references the endpoint behavior added in step 2.

In this post, we will complete the development of this HI_REST GET service operation. The path we will take will be to briefly describe and implement a plain vanilla WCF Service Contract and implementation. I will then make some changes to RESTify the operation. Let’s get started with describing the Service Contract in IRESTCatalogService.cs and the implementation in RESTCatalogService.cs.

IRESTCatalogService.cs

This file contains the service contract. Essentially, the service contract defines what the service does or, in other words, what operations the service exposes. There are 2 ways to define a service contract:

Decorate a class with a ServiceContractAttribute. Every operation in the class decorated with an OperationContractAttribute will be exposed as an operation of the service.

Decorate an interface with a ServiceContractAttribute. Every operation in the interface decorated with an OperationContractAttribute will be exposed as an operation of the service.

You may remember that the ‘AJAX-enabled WCF Service’ template we used in Part II used the first method, decorating the class. This approach did not require an interface and therefore eliminated the need for an entire file (the interface file). In this case the interface is simply inferred. While this approach is clearly more simple (1 less file, no need to understand interfaces), it is also less flexible. In most cases, option 2 is a better choice because it clearly separates the contract from the implementation, allowing you to easily change the implementation should the need arise.

When you add a ‘WCF Service’, the template uses approach 2. IRESTCatalogService.cs contains our interface, decorated with the ServiceContractAttribute. It further contains one method decorated with an OperationContractAttribute (see above). Obviously, this is just a stub for our real service contract which we are going to add now. In this post, we are illustrating a fetch. So we will implement an operation that fetches a single product that matches the product name passed in. If you remember back to Part II, I defined a ProductData type. This will be the return type for our operation. I will redefine it here:

Here is our starting point. We will make changes to this as we continue along…

I decided to use LINQ to SQL for this demo in order to keep it simple. I chose to use the SingleOrDefault extension method to return the single product. This extension method returns the single match for the condition or the default value which is null for value types. If more than one match exists, an exception is thrown. I made a questionable decision in my implementation in that I chose to throw an exception if the product did not exist. Exceptions are best used only when an unexpected condition occurs in your code. A non-match is hardly unexpected. Fear not, I will address this as we move along.

HI-RESTifying the service

I have a list of goals we want to accomplish to convert this service operation to HI-REST. The goals are as follows:

Use the appropriate HTTP Verb

Choose and implement an appropriate Representation Format

Use Response Codes to tell the caller success / failure

Ensure Uri Addressablity

Let’s attach them one-by-one:

Appropriate HTTP Verb

Well, this should be a no-brainer. We are doing a fetch, so we want to use an HTTP GET. The way in which we accomplish setting the HTTP verb is to decorate the service operation (along with the OperationContractAttribute) with one of two attributes: WebGetAttribute ([WebGet]) or WebInvokeAttribute ([WebInvoke]). Decorating with WebGet associates the operation with the HTTP GET verb. Decorating with WebInvoke allows you to associate the operation with any other HTTP VERB (PUT, DELETE, POST, OPTIONS, etc.) by setting the Method parameter of the attribute. We will see this in future posts when I illustrate an actionable service operation.

It should be clear that all I need to do here is to decorate my service operation with a WebGet. The service operation will be found in IRESTCatalogService.cs. It should now look like this:

Appropriate Representation Format

The discussion of what an "appropriate" representation format is is out of the scope of this post. Perhaps I’ll attack this in a later post (after some beer). The reason this is a difficult topic is that differing folks have differing opinions on what appropriate is. For example, the intent is clear for an ATOM formatted representation. What I mean by this is that it is clear what the "title" element contains… a title. Is a JSON formatted representation as clear? Certainly we can agree that JSON is an agreed upon set of formatting rules for the JavaScript standard. However, if we represent an object as JSON, is the intent as clear. Suppose I represent an object with 2 properties (name and title) as JSON. What is the intent of title? It could be a book title or a person’s title. Without additional information, the intent is unclear. As I stated, I am not going to get into what an appropriate representation format is in this post. I simply wanted to surface the issue.

In the case of our simple example here, I am going to choose POX, for now. POX stands for ‘plain old XML’. If you buy into the REST continuum I brought up in Part I of this series, POX would move us toward the LO-REST side. We will convert this to JSON in the next post and to RSS and ATOM in subsequent posts, so worry not. For now, all you need to know is that you set the ResponseFormat parameter of the WebGetAttribute (or WebInvokeAttribute for that matter) to WebMessageFormat.Xml. The service contract should now look like this:

Response Codes to alert success or failure

HTTP has a rich set of defined status codes. The intent is that these are returned in the header of the response to indicate to the caller whether the call was a success or why it failed. We want to include this functionality in our HI-REST service operation. In order to accomplish this, we are going to need to be able to get a reference to the outgoing response. System.ServiceModel.Web has a helper class called WebOperationContext that provides easy access to the properties of the request and response of these web friendly calls. We simply need to access the Current static property to get access of the context.

WebOperationContext ctx = WebOperationContext.Current;

Once we have a reference to the context, we can get a reference to the outgoing response and set the status code appropriately. In the case of this example, if all goes well, I will set the status code to HttpStatusCode.OK or HTTP 200. If a product is not found, I will set the status code to HttpStatusCode.NotFound or HTTP 404. Lastly, if I don’t know what went wrong, I will set the status code to BadRequest or HTTP 400. Here is the updated code in my implementation:

Ensure Uri Addressablity

One of the key tenets to REST is the notion of URI Addressability. To put it simply, you treat everything as a resource and each resource has it’s own unique URI. WCF now has a simple means of making service operations URI Addressable. It is accomplished with a UriTemplate. A complete overview of UriTemplates can be found on MSDN. For the purpose of this post, you can simply think of a UriTemplate as a way to represent or describe a group of relative URIs with a pattern expression. The UriTemplate grammar allows you to define variables and if you follow the appropriate naming conventions, the variable will be mapped to arguments in your service operation. This is best illustrated with an example. The following is the WebGetAttribute updated with our UriTemplate:

Notice the UriTemplate contains a segment with ‘{productName}’. The curly braces allow you to define a variable. Also note that the variable name is the same as the argument to the operation. The infrastructure will perform this mapping for you. (For more information take a look at Steve Maine’s blog post on UriTemplate.Match)

The template is relative to the address of our service. Let’s assume that we are using Cassini and the following is the URI to our svc file:

http://localhost:50183/ClientStore/RESTCatalogService.svc

The following URI would map to our service operation, setting the productName argument to ‘EelWoo’:

The capabilities of the UriTemplate give us the ability to make our service operations URI addressable. Some people take exception to the fact that, because the templates are relative and in IIS and WAS, the *.svc file acts as the address to the endpoint, ‘.svc’ is always in the URI and this is not very clean (or what I refer to as hackable). I will address this concern in future posts, showing you a few ways of eliminating this from your URI.

Testing Our Service Operation

Well, we have accomplished our 4 goals and now it is time to test the solution. We may not have hit every aspect that makes a service HI-REST, but given our earlier discussion of the REST continuum, I believe I have provided an implementation that clearly leans on the HI-REST side. I will be doing future posts that may address any concerns you may have with the current implementation (please send me any issues you may have and I will do my best to address them), but for now, it is what it is.

In order to test, I can simply type the following URI into the browser: http://localhost:50183/ClientStore/RESTCatalogService.svc/product/EelWoo. What I get back is the following XML:

Sorry, at one point, both my VM at my ISP and their backup server died. I lost everything. I actually rebuilt this site from old caches of the posts in livewriter or on google cached pages. The code, however was not recoverable.

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!