http://www.developer.com/

Back to article

Creating RESTful Web Services with Windows Communication Foundation


August 21, 2007

Windows Communication Foundation (WCF) is Microsoft's services-oriented framework for building connected systems using the Microsoft .NET Framework. Although WCF is flexible enough to build arbitrary services-based solutions, its primary story is Web Services. To get the most out of this article, you should have at least a basic understanding of core WCF concepts including messaging, serialization, contracts, and bindings/endpoint configuration. This article was written based on .NET 3.5, the Beta 2 release.

Prior to (and including) version 3.5 of the .NET Framework, web services—whether ASMX or WCF—have been implemented using SOAP and the WS-* stack. The WS-* stack is a suite of extensions to the SOAP specification that define advanced systems-level functionality such as transactions, reliable messaging, and more. A web service was typically accessed by performing an HTTP POST to the service endpoint (for example, "http://www.example.com/ExampleService.svc") and sending a SOAP message as the submitted data. Because HTTP is a request-reply protocol, the response to the HTTP request contained a SOAP message describing the relevant response from the server. This approach to web services is by far the most prevalent in use today, and has many compelling features that make it a viable solution to many business problems. However, this approach hijacks features of the HTTP protocol, making it a "carrier" for a completely separate protocol (SOAP). For several years, a discussion has been growing over the idea that the HTTP protocol itself is rich enough to offer the capabilities that many web services require, while simultaneously providing the benefits such as scalability that the World Wide Web has enjoyed for years—benefits that a SOAP-based web service implementation does not enjoy "for free."

In .NET 3.5, WCF offers support for both worlds of programming: creating services that can be exposed via a SOAP endpoint, or a "RESTful" endpoint, depending on configuration. An alternate phrase for "REST" is "web programming." REST stands for Representational State Transfer, a term coined by one of the original authors of the HTTP protocol, Roy Fielding, in his 2000 doctoral dissertation.

Representational State Transfer

Representational State Transfer is a phrase that describes the generic principles that make up much of what we know to be the HTTP protocol. As it applies to web services, the primary concepts of interest are resources, and the actions that can be taken on a given resource. Resources are identified by Uniform Resource Identifiers (URIs), something that nearly everybody is familiar with. SOAP-based web services typically use a single URI to represent the service endpoint plus an operation. A RESTful web service uses a unique URI to reference every resource, and HTTP verbs to define actions on those resources. As an example, I will reference a simple blog engine—the source code is available for download. At the very basic level, a blog consists of posts and comments made on those posts. Retrieving a single post (a resource) using SOAP-based web services would use URI syntax much like this:

"http://blogexample.com/Blog.asmx?op=GetPost"

The URI simply serves to identify the service endpoint and the operation to invoke, not the actual resource (the specific blog post). The identifier of an individual post to retrieve would be specified inside the SOAP message contained in the body of the HTTP request. A RESTful web service would use a combination of a truly unique URI and an HTTP verb to retrieve a blog post; for example, performing an HTTP GET request to "http://blogexample.com/blog/post/123456" to retrieve blog post number 123456.

The idea of the uniqueness of the URI is of key importance. Each resource should receive its own URI. Other factors, such as the "purity" of the URI can also be important, but less so than uniqueness. The second most important facet of a RESTful web service is the use of HTTP status codes. HTTP status codes (including a "description" field) define the client-side success/error reporting mechanism. Care should be given to ensure that the appropriate status code is returned from a web request.

"URI purity", as I define it, refers to the degree with which implementation details "leak" into the URI of a resource. For example, when hosting a WCF web service in Internet Information Services (IIS, discussed later in the article) any URIs must include the service file, with a ".svc" extension. For example, retrieving blog post number 123456 might use the following URI: "http://blogexample.com/blog.svc/post/123456". The ".svc" extension is an implementation-specific detail that "muddies" the URI. If the underlying implementation changes to, say, use Ruby on Rails, the URI for the same resource would likely change. It is worth noting that "URL rewriting" can help resolve some of the implementation-specific idiosyncrasies.

What's New in WCF

"That's all fine and good", you're thinking, "but you still haven't told me anything about implementing RESTful services with WCF." Excellent point—enough background, it's time to dig in. There are several classes and attributes that have been added which extend the existing service model allowing services to be exposed as RESTful services.

System.UriTemplate

At a basic level, the UriTemplate class supports the matching or binding of a string template with name-value pairs. When defining an OperationContract, a UriTemplate is specifiedl it accomplishes two purposes. First, it provides the mapping between a request URI and an OperationContract, and second, it identifies the named values that are subsequently bound to matching OperationContract method parameters. An example of a UriTemplate instance looks like the following:

UriTemplate template =
   new UriTemplate("/post/{postid}/comments?maxresults={maxresults}");

There are some important things to know about the semantics of a UriTemplate:

  • Variable segments or parameters are specified as named values surrounded by curly braces, as in "{postid}" above. After a match, the value represented by "{postid}" will be available under the key "postid".
  • A variable must encompass an entire segment. For example, it is not valid to specify "/post/post-{id}-format/comments" as a template.
  • Query string parameter names must be literal; only the value can be parameterized.
  • A wildcard ("*") may be specified at the end of the URI template to indicate that the template matches "subsets".
  • When used in conjunction with an OperationContract, the OperationContract parameters that match the UriTemplate must be string types.

System.ServiceModel.Web.WebGetAttribute and System.ServiceModel.Web.WebInvokeAttribute

The WebGet and WebInvoke attributes are declared on an OperationContract. They use a UriTemplate, along with some additional information, to associate an HTTP request with an OperationContract and to optionally provide fine-grained control over the serialization/deserialization of the HTTP request/response data.

WebGet allows the following Properties to be specified:

Property Name Type Possible Values Description
BodyStyle WebMessageBodyStyle

Bare
Wrapped
WrappedRequest
WrappedResponse

Controls aspects of the formatting of the serialized data.
RequestFormat WebMessageFormat

Json
Xml

Specifies the deserialization format for incoming HTTP requests.
ResponseFormat WebMessageFormat

Json
Xml

Specifies the output serialization format to use for an individual OperationContract.
UriTemplate String Any string representing a parameterized relative URI The template that will be matched against incoming HTTP request URIs.

WebInvoke provides the same properties as WebGet, but with one additional property, "Method". Note that when hosting your service in IIS using the WebScriptServiceHostFactory (discussed later), the only valid "Method" values are "GET" and "POST". I am unsure if this is a restriction due to the fact that .NET 3.5 is in Beta 2, or if the restriction will remain for the official release.

Property Name Type Possible Values Description
Method String "POST", "DELETE", "*", and so forth. The HTTP verb that must be specified for an incoming request to match the given OperationContract.

An OperationContract that retrieves a list of blog posts might be represented as follows:

[OperationContract]
[WebGet(UriTemplate = "/posts")]
PostResultSet GetPosts();

An example of an OperationContract that updates an existing blog post might be represented as follows:

[OperationContract]
[WebInvoke(Method = "PUT", UriTemplate = "/admin/post/{id}")]
void UpdatePost(string id, Post post);

You can see from this example that the string parameter "id" is bound to the UriTemplate's value for "{id}". Remaining parameters on an OperationContract method marked with the WebGet/WebInvoke attributes will attempt to deserialize the data within the HTTP request. Because these examples do not specify the RequestFormat or ResponseFormat properties on either the WebGet or WebInvoke attributes, the default serialization configured for the endpoint will be used.

System.ServiceModel.Web.WebOperationContext

The WebOperationContext is available within an OperationContract implementation via the WebOperationContext.Current property. It exposes four "contexts" as properties, each of which relate to the HTTP request or response within the WCF request/response model: IncomingRequest, IncomingResponse, OutgoingRequest, and OutgoingResponse. The two that will typically be used the most are IncomingRequest and OutgoingResponse.

The IncomingRequest property is important because it offers access to web-specific properties of the request that was mapped to the OperationContract implementation. This allows inspection of data such as ContentType, ContentLength, Method (GET, POST, PUT, and so on), UserAgent, and UriTemplateMatch. This information can be used to invoke specific processing for various conditions. For example, in a blog service, a URI of "/posts" retrieves a list of posts, unfiltered and unrestricted. However, for performance and scalability reasons, a blog that contains hundreds or thousands of posts should not return all of them by default. One possible solution is to use defaults defining a "paged" response of posts, and allowing query string parameters within the URI to override the defaults. Information about the URI that matched the UriTemplate is available in the UriTemplateMatch property. Given the following as the OperationContract for a "GET /posts" HTTP request:

[OperationContract]
[WebGet(UriTemplate = "/posts")]
PostResultSet GetPosts();

You can create the following implementation to allow a limited subset as a default response, with query string parameters that can override the defaults. Note that I've moved common processing such as query string parameter retrieval into a static helper class:

public PostResultSet GetPosts()
{
   int start;
   int maxresults;
   Utilities.GetPageInfo(out start, out maxresults, true);

   if ((start >= 0) && (start <= _posts.Count)
      && (maxresults > 0))
   {
      // Set the upper limit to the smaller of the two relevant values
      maxresults = Math.Min(maxresults, _posts.Count);

      // Select all posts that fall within the range provided,
      // ordered by the date they were modified
      IEnumerable<Post> posts = from post in _posts
                                orderby post.ModifiedOn
                                select post;

      // Return the page specified by the querystring parameters
      // (or defaults)
      posts = posts.Skip<Post>(start).Take<Post>(maxresults);

      PostResultSet postResultSet = new PostResultSet();
      postResultSet.Posts = posts.ToList<Post>();
      postResultSet.Start = start;
      postResultSet.TotalCount = _posts.Count;
      return postResultSet;
   }
   else
   {
      WebOperationContext.Current.OutgoingResponse.StatusCode =
         System.Net.HttpStatusCode.BadRequest;
      // Return an empty list
      return new PostResultSet();
   }
}

public static class Utilities
{
   public static void GetPageInfo(out int start,
                    out int maxresults,
                    bool capMaxResultsDefault)
   {
      if (!GetFromQueryString("start", out start))
      {
         start = 0;
      }

      if (!GetFromQueryString("maxresults", out maxresults))
      {
         maxresults = (capMaxResultsDefault) ? 25 : Int32.MaxValue;
      }
   }

   public static bool GetFromQueryString(string name, out int value)
   {
      value = 0;
      WebOperationContext context = WebOperationContext.Current;
      UriTemplateMatch uriMatch =
         context.IncomingRequest.UriTemplateMatch;
      string strValue = uriMatch.QueryParameters[name];

      if (!String.IsNullOrEmpty(strValue))
      {
         return Int32.TryParse(strValue, out value);
      }

      return false;
   }
}

The OutgoingResponse property exposes an OutgoingWebResponseContext object that encapsulates the response StatusCode, StatusDescription, ContentType, ContentLength, and any other HTTP header value. It includes helper methods for setting the StatusCode to typical values: SetStatusAsCreated and SetStatusAsNotFound. For example, an OperationContract implementation that creates a new blog post should set the response StatusCode to 201 (Created) along with the URI of the created resource:

public void CreatePost(Post post)
{
   WebOperationContext.Current;

   if (post == null)
   {
      context.OutgoingResponse.StatusCode =
         System.Net.HttpStatusCode.BadRequest;
      return;
   }

   post.Id = GetNextPostId();

   UriTemplateMatch match =
      context.IncomingRequest.UriTemplateMatch;
   UriTemplate template = new UriTemplate("/post/{id}");
   post.Uri =
      template.BindByPosition(match.BaseUri, post.Id.ToString());

   _posts.Add(post);

   context.OutgoingResponse.SetStatusAsCreated(post.Uri);
}

Bindings, Behaviors, and Hosts

The new System.ServiceModel.WebHttpBinding class is responsible for preventing any automatic SOAP formatting for WCF messages, by specifying MessageVersion.None. It also performs the work required to connect the service to HTTP or HTTPS, similar to System.ServiceModel.BasicHttpBinding. System.ServiceModel.Description.WebHttpBehavior is where the bulk of the work is performed. The custom behavior uses the WebGet and WebInvoke attributes to make connections between HTTP requests/responses and OperationContracts. It provides the formatting and serialization implementations, as well as validation and other related work. WCF offers some helper classes to minimize the configuration impact of hooking up an endpoint that will serve "WebHttp" functionality. For most scenarios, these helper classes should be sufficient to establish a working endpoint. The primary helper is System.ServiceModel.Web.WebServiceHost, which automatically adds a WebHttpBehavior using the default settings. Creating a service using the WebServiceHost is as simple as specifying the desired binding. For example, the following three lines of code create and open a RESTful service endpoint at "http://localhost:8000/blog":

WebServiceHost host =
   new WebServiceHost(new BlogService.BlogService(),
                      new Uri("http://localhost:8000/blog"));
host.AddServiceEndpoint(typeof(BlogService.IBlogService),
                        new WebHttpBinding(), "");
host.Open();

To make things even easier, two ServiceHostFactory implementations (new in .NET 3.5, but outside the scope of this article) have been included in WCF. When hosting a service in IIS, using the WebServiceHostFactory and WebScriptServiceHostFactory will automatically configure bindings, behaviors, and endpoints using the appropriate information from the IIS metabase. The difference between the two is that WebScriptServiceHostFactory will set defaults (JSON) and create restrictions (GET and POST only) such that it "plays nice" with ASP.NET AJAX out-of-the-box, and it creates a WebScriptServiceHost object that is marked internal, meaning the only way to create one is with the factory class.

Creating a Service

To demonstrate "web programming" (REST) with WCF, I have created a simple blog engine. It has no backing store, meaning a restart of the service will wipe all existing data, it is a singleton service that is single threaded (for simplicity), and it supports simple post and comment management only. Posts and comments can be created, read, updated, or deleted—this sort of functionality is frequently referred to as CRUD (Create, Read, Update, Delete). The example demonstrates some of the concepts discussed in this article, including using the WebGet and WebInvoke attributes and the WebOperationContext class, multiple hosting options, and finally the concept of returning different serialization formats for different URIs. In the case of the last feature, returning different serialization formats, I've chosen to use a scheme where the standard URI returns the format configured for the endpoint, and appending the desired format to the end of the URI will cause the response to be rendered in the requested format. For example, the following table shows several URIs that will all retrieve the same blog post, but in varying formats:

URI Response Format
/blog/post/456 Dependant on the endpoint configuration
/blog/post/456/json JSON
/blog/post/456/xml XML
/blog/post/456/rss RSS

Formatting results as RSS is outside the scope of this article, but the included example should give a good idea of how easy WCF 3.5 makes it to return syndicated data. To run the sample with the least amount of setup, simply run the BlogServer project or application. In a browser, navigate to "http://localhost:8000/blog/posts" to see the XML response from the service.

Conclusion

Windows Communication Foundation makes impressive strides ahead in version 3.5 by offering fully integrated support for web programming. The semantics of the programming model along with out-of-the-box configuration defaults make creating and hosting "RESTful" web services very simple. WCF allows service creators to expose services using SOAP and REST, increasing their value by catering to the needs and desires of the service consumers—the customer. So, tag your service with WebGet/WebInvoke and enjoy the REST.

Download the Code

You can download the code that accompanies this article here.

Acronyms

  • REST: Representational State Transfer
  • WCF: Windows Communication Foundation
  • HTTP: Hypertext Transfer Protocol
  • ASMX: The ASP.NET implementation of Web Services—ASMX is the file extension used on the "endpoint"
  • JSON: Javascript Object Notation
  • XML: Extensible Markup Language
  • RSS: Really Simple Syndication, Rich Site Summary, or RDF Site Summary—depending on who you ask

References

  1. http://en.wikipedia.org/wiki/REST: Wikipedia entry on REST
  2. http://msdn2.microsoft.com/en-us/library/aa395208.aspx: REST and POX with WCF (3.0)
  3. http://hyperthink.net/blog: Steve Maine, Program Manager for Microsoft
  4. http://www.developer.com/net/net/article.php/3668901: A Primer to Windows Communication Foundation (3.0)
  5. http://www.developer.com/net/net/article.php/3676161: Building WCF Channels and Bindings (3.0)

About the Author

Aaron Lerch is a Software Developer and Team Lead for Interactive Intelligence, Inc. in Indianapolis, Indiana, USA. By day, he leads a team working on Windows Forms/WPF applications and by night, he dives into WCF, PowerShell, ASP.NET, and any other interesting .NET-based technologies. He keeps a blog about his development experiences at http://www.aaronlerch.com/blog.

Sitemap | Contact Us

Thanks for your registration, follow us on our social networks to keep up-to-date