WCF and REST

Design and Implementation Considerations for WCF and RESTful Services

This month we ll explore how to design a RESTful service using the new features in .NET 3.5 for WCF. For more information on WCF, see "Security Practices for WCF " and " ASP.NET and WCF ."

Web 2.0 programming models are more popular than ever. Rich internet applications (RIAs) developed with JavaScript (AJAX) or Silverlight rely on service developers to support Plain-Old-XML (POX) or JavaScript Object Notation (JSON) because they are much simpler to work with than SOAP Web services. Services based on the REST paradigm fit nicely with the POX and JSON model, thus are also an important part of Web 2.0.

Representational State Transfer (REST) is an architectural approach for designing services based on a few simple principles. First, application resources are made available through intuitive URL patterns. Second, HTTP verbs are used to indicate the action to be taken on a resource. In this article I ll explain how to leverage WCF features introduced in .NET 3.5 which I discussed in the March and April columns to implement RESTful services in support of Web 2.0 applications.

 

.NET 3.5 Features for REST

In the past few columns I explained how to create WCF services using the new features of the Web programming model. Specifically, I discussed features introduced in .NET 3.5 to support POX and JSON services, such as:

  • Applying the WebGetAttribute and WebInvokeAttribute to service operations to configure how to request a URI map to service operations, to configure the XML or JSON messaging format, and to select the correct HTTP verb that triggers an operation.
  • Configuring endpoints that use the new WebHttpBinding, which supports the Web programming model.
  • Adding the WebHttpBehavior, which configures the service model to use URI pattern matching to invoke service operations, along with a number of other features that bypass default SOAP messaging semantics.
  • Using WebServiceHost and WebServiceHostFactory to automatically enable the WebHttpBehavior for WebHttpBinding endpoints.

These features also play a role in RESTful WCF services.

 

Comparing POX and REST

POX and REST services have a lot in common in the sense that they both rely on URL pattern matching to invoke operations. Where they differ is in the approach to how those URLs are defined, and which HTTP verbs are used to invoke operations at each URL. Consider the two services illustrated in Figure 1. Both services expose these service operations: GetCustomers, CreateCustomer, UpdateCustomer, and DeleteCustomer.


Figure 1: Comparing POX and REST services.

The POX service uses HTTP GET to retrieve values and HTTP POST to invoke operations that require some information be passed. The URI to trigger each operation is named the same as the operation. This is not a requirement, or a recommendation, but it is a default behavior when you apply WebGetAttribute or WebInvokeAttribute to an operation without specifying otherwise.

By comparison, the REST service uses naming conventions for URI that match the application resource, rather than the service operation name. HTTP GET, POST, PUT, and DELETE are used, respectively, to retrieve resources, create new resources, update resources, or delete resources. Put another way, each verb indicates an action to be performed on the resource indicated by the URL.

Notice that because the service operations are the same for each service, it is merely the URL that differs for triggering the operation. This is part of the service contract design.

 

Designing a REST Service Contract

Designing a REST service contract begins with identifying the application resources that will be accessed through the service. A very simple contract may only expose a single resource, while a more complex contract may need to deal in hierarchies of resources that require some thought be put into the URL patterns to support the hierarchy. For example, to interact with Customers you might support CRUD operations through the relative URLs shown in Figure 2.

URL

Functionality

GET /Customers

Return the Customers collection.

GET /Customers/{id}

Return a specific customer record.

POST /Customers

Create a new customer. Implies the HTTP POST includes content representing the customer details.

PUT /Customers/{id}

Update a specific customer record. Implies the HTTP POST includes content representing the customer details.

DELETE /Customers/{id}

Delete a specific customer record.

Figure 2: HTTP verbs, relative URLs, and related functionality to interact with Customer resources.

The service contract to support this is shown in Figure 3. The UriTemplate property of the WebGetAttribute and WebInvokeAttribute indicate the format of the URL required to invoke each operation. The Method property of the WebInvokeAttribute indicates if POST (the default), PUT, or DELETE should be the HTTP verb to trigger the operation. For the service model to map a request for a particular URL to an operation, the combination of UriTemplate and Method must be unique.

  [ServiceContract(Namespace="http://www.thatindigogirl.com/

 samples/2008/01")]

public interface ICustomersService

{

  [OperationContract]

  [WebGet(UriTemplate = "Customers")]

 List GetCustomers();

  [OperationContract]

  [WebGet(UriTemplate="Customers/{id}")]

 Customer GetCustomer(string id);

  [OperationContract]

  [WebInvoke(UriTemplate = "Customers", Method = "POST")]

 void CreateCustomer(string name);

  [OperationContract]

  [WebInvoke(UriTemplate = "Customers/{id}", Method = "PUT")]

 void UpdateCustomer(string id, Customer customer);

  [OperationContract]

  [WebInvoke(UriTemplate = "Customers/{id}", Method = "DELETE")]

 void DeleteCustomer(string id);

}

Figure 3: The service contract describing the functionality from Figure 2.

Additional UriTemplate definitions can be mapped to new service operations to support resource hierarchies. For example, to interact with customer orders you might need to support additional requests, as shown in Figure 4.

URL

Functionality

GET /Customers/{id}/Orders

Returns the Orders collection for a particular customer.

GET /Orders/{id}

Returns a specific order.

GET /Customers/{id}/Orders/{id}

Returns a specific order if it belongs to a specific customer.

POST /Customers/{id}/Orders

Creates an order for a specific customer.

PUT /Customers/{id}/Orders/{id}

Updates an order record for a specific customer.

DELETE /Orders/{id}

Deletes a particular order.

Figure 4: Defining URLs to access resource hierarchies.

 

Dealing with Complex Hierarchies

If your application exposes deep hierarchies of data, or a large number of different resources, you can imagine that the number of required UriTemplate definitions and related service operations can grow significantly and quickly become unmanageable. ADO.NET data services (part of ASP.NET 3.5 Extensions) is a good example of this because it can provide RESTful access to an entire Entity Framework or other IQueryable set of resources. The WebDataService (a WCF service) used to access those resources implements a very simple service contract that can receive any UriTemplate, and any HTTP verb as follows:

  [ServiceContract]

public interface IRequestHandler

{

  [OperationContract]

  [WebInvoke(UriTemplate="*", Method="*")]

 Message ProcessRequestForMessage(Stream messageBody);

}

In fact, the service implementation for this single method does the following:

  • Looks at the HTTP Content-Type header to determine how to process the request stream.
  • Looks at the HTTP Accept header to determine what format the response should be returned with (i.e., XML, JSON, SOAP).
  • Processes the UriTemplate to determine what resource to access.
  • Uses the Method (HTTP verb) to determine what action should be taken on the resource.

This is an extreme example that requires the ultimate level of flexibility but it is worth noting that by manipulating the contract you can centralize processing rather than defining complex service contracts.

 

Overriding WebErrorHandler

Service operations or related downstream business logic may throw an exception during execution. Traditionally, business logic will throw exceptions using CLR types. With SOAP messaging, those should be converted to a type of FaultException in order to preserve the communication channel and to explicitly control the information reported to clients. REST services rely on the WebHttpBehavior to map requests from URI to service operation but this behavior also injects some default error handling semantics that you may want to customize.

By default, WebHttpBehavior adds an error handler to the service endpoint to which it is applied. This WebErrorHandler type intercepts all exceptions (including FaultException) and converts them to an HTML stream that can be presented in the browser. If the service enables sharing exception details through the ServiceDebugBehavior, the resulting page will show the stack trace of the exception. Of course, this is not a recommended practice for production, and so the default behavior is not to share any details about the exception. In either configuration, this default error handler is not terribly useful for REST services.

At a minimum, you want to provide an informative message to clients. Traditionally, REST services rely on HTTP headers to report errors, and may optionally provide additional content in the response. To accomplish this you must first disable the default WebErrorHandler and then write code to send the appropriate response. The best way to do this is by adding a custom error handler that will handle the response.

When you create a ServiceHost instance using WebServiceHost or WebServiceHostFactory, the WebHttpBehavior is added to WebHttpBinding endpoints. WebHttpBehavior adds the WebErrorHandler in its AddServerErrorHandlers method as follows:

protected virtual void AddServerErrorHandlers(ServiceEndpoint

 endpoint, EndpointDispatcher endpointDispatcher)

{

 endpointDispatcher.DispatchRuntime.ChannelDispatcher.

 ErrorHandlers.Add(new WebErrorHandler(endpointDispatcher.

 DispatchRuntime.ChannelDispatcher.

 IncludeExceptionDetailInFaults));

}

To suppress the addition of this error handler, extend WebHttpBehavior and override this method to add your own error handler:

public class WebHttpBehaviorFaultErrorHandler :

 WebHttpBehavior

{

 protected override void AddServerErrorHandlers(

 ServiceEndpoint endpoint,EndpointDispatcher

 endpointDispatcher)

 {

 endpointDispatcher.ChannelDispatcher.ErrorHandlers.Add(

  new FaultErrorHandler());

 }

}

FaultErrorHandler is a custom error handler that implements IErrorHandler. In its ProvideFault method it sets the outgoing HTTP headers to indicate the status code and description, then initializes the fault message parameter to include the original error message (see Figure 5).

public void ProvideFault(Exception error, MessageVersion version, ref Message fault)

{

 WebOperationContext.Current.OutgoingResponse.StatusCode = HttpStatusCode.BadRequest;

 WebOperationContext.Current.OutgoingResponse.StatusDescription = error.Message;

 WebOperationContext.Current.OutgoingResponse.ContentType = "application/xml; charset=utf-8";

 fault = Message.CreateMessage(version, FaultCode.CreateSenderFaultCode(error.Source,

"http://www.thatindigogirl.com/samples/2008/01"), error.Message, null);

}

Figure 5: The ProvideFault method of the FaultErrorHandler custom error handler.

Assuming the service threw the following exception, the resulting HTTP response would look like Figure 6:

throw new InvalidOperationException("No customers to
return."); 

The status code and description are the most important part of this response, as this ensures that the original error message is easily accessible to calling clients. Otherwise, the default behavior is to suppress any exception details. In this case, the error handler sets the HTTP status code to BadRequest for all exceptions, which is equivalent to HTTP status 400. The description always includes the original error message.

HTTP/1.1 400 No customers to return.

Content-Length: 281

Content-Type: application/xml; charset=utf-8

Server: Microsoft-HTTPAPI/2.0

Date: Thu, 27 Mar 2008 22:30:27 GMT



Sender



a:Services

No customers to return. 

Figure 6: Throw an exception to get this HTTP response.

As for the message fault, this is returned in the HTTP response content, so the error handler sets the content type header to application/xml . Clients that care to inspect the content can pull additional details from the section but traditionally this is not used by RESTful clients.

If you don t care to return the additional fault content, you can suppress this by setting SuppressEntityBody to true on the OutgoingResponse:

WebOperationContext.Current.OutgoingResponse.

 SuppressEntityBody = true; 

The resulting HTTP response would be simplified to:

HTTP/1.1 400 No customers to return.

Content-Length: 0

Server: Microsoft-HTTPAPI/2.0

Date: Thu, 27 Mar 2008 22:34:46 GMT

As an alternative to throwing a generic HTTP 400 exception for each exception thrown, you also can set the same OutgoingResponse properties using WebOperationContext prior to throwing a new FaultException. For example, the GetCustomers operation shown in Figure 7 would produce the same result (if the WebErrorHandler is disabled).

public List GetCustomers()

{

 if (s_customerStorage.Count == 0)

 {

   WebOperationContext.Current.OutgoingResponse.ContentType =

      "application/xml; charset=utf-8";

   WebOperationContext.Current.OutgoingResponse.StatusCode =

      HttpStatusCode.BadRequest;

   WebOperationContext.Current.OutgoingResponse.StatusDescription =

     "No customers to return.";

   throw new FaultException("No customers to return.", FaultCode.CreateSenderFaultCode("CustomersService", "http://www.thatindigogirl.com/samples/2008/01"));

 }

 return new List(s_customerStorage.Values);

}

Figure 7: An alternative to throwing a generic HTTP 400 exception.

 

The advantage here is that you can explicitly set the appropriate HTTP status code instead of defaulting to HTTP 400 (or some other value) in the common error handler. In addition, by throwing a FaultException, a properly formatted will be included in the response content.

 

Mixing REST and SOAP

What happens if you want to host the same service over both SOAP and REST protocols? In fact, a single service contract and implementation can handle both types of requests. What distinguishes the two is primarily message filtering and message encoding:

  • REST relies on the service model to map a URL to a particular operation; SOAP relies on the Action header inside the message.
  • REST messages can be formatted as XML or JSON; SOAP messages can be serialized as binary, XML, or MTOM.

By decorating your contracts with WebGetAttribute or WebInvokeAttribute you are able to specify the correct UriTemplate for mapping URLs to operations. The same attributes make it possible to specify XML or JSON formatting. These attributes are ignored unless you enable the WebHttpBehavior for an endpoint. Thus, if you expose two endpoints for the same service contract, you can configure one for REST and another for SOAP protocols. The address for each must be unique, as shown here:



 

 

 

 

  

Notice that the configuration uses WebHttpBinding for the /Rest endpoint, while the /Soap endpoint uses WSHttpBinding. You can expose both endpoints using the WebServiceHost instead of ServiceHost and only those endpoints using WebHttpBinding will include the WebHttpBehavior. The following code to self-host would still support REST and SOAP endpoints:

WebServiceHost host = new WebServiceHost(

 typeof(Services.CustomersService));

host.Open();

The same result applies if you use WebServiceHostFactory for .svc endpoints.

 

Security and REST Services

SOAP Web services provide a built-in set of features for transport and message security that can be as simple as username and password authentication and as rich as federated security. Security for REST services today is primarily limited to HTTP authentication or HTTP cookies. With HTTP authentication, callers can send basic, digest, Windows, or certificate credentials to authenticate each call. HTTP cookies can be assigned after a caller is authenticated using ASP.NET authentication, or after HTTP authentication has been completed.

Using WebHttpBinding you can enable HTTP authentication for a service endpoint by enabling one of the two available security modes: Transport or TransportCredentialOnly. Transport security requires an SSL certificate be present, while TransportCredentialOnly sends credentials without SSL. In either case, the choice of credentials are defined by the HttpClientCredentialType values listed in Figure 8.

HttpClientCredentialType

Description

None

No credentials provided.

Basic

Username and password credentials are sent in plain text.

Digest

Username and password credentials are sent securely.

Kerberos

Assumes a Windows domain is present and that Windows credentials will be provided.

Ntlm

Windows credentials are provided but a Windows domain is not required.

Windows

Windows credentials are provided. If a domain is present, Kerberos is used; otherwise, Ntlm.

Certificate

An X.509 certificate must be provided.

Figure 8: Credential options for WebHttpBinding security.

Figure 9 shows the service model configuration for a service that requires digest authentication over SSL.



 

   

     

   

 

 

   

     

       

         

       

     

   

 

 

   

     

       

         

       

       

     

   

 

 

Figure 9: Configuration for HTTP digest authentication.

 

By default, credentials would be authenticated and authorized against the Windows domain, but in this case, credentials are authenticated with a custom UserNamePasswordValidator type. The point is that credentials passed over HTTP authentication can be authenticated and authorized in a similar way to SOAP Web services: using the Windows domain, the ASP.NET provider model, or other custom mechanisms supported for the credential type.

In addition, as with SOAP Web services, the ServiceSecurityContext is initialized with the correct user identity and the executing thread is initialized with a security principal commensurate to the ServiceAuthorizationBehavior configuration. That means that any role-based authorization code used for SOAP can also be used for REST services providing they work with the same credential type and authorization policy.

A less desirable alternative to HTTP authentication is to protect REST services using ASP.NET authentication which means enabling ASP.NET compatibility mode for the service. With this approach, requests for services are first processed through the ASP.NET pipeline. Windows or forms authentication can be used to protect access to services, and, once authenticated, an authentication cookie can be provided for subsequent calls.

 

Conclusion

This article really only scratches the surface of features that we could discuss related to designing REST services with WCF. There are many advanced features to consider related to supporting XML and JSON from the same service implementation, URL rewriting techniques that come in handy for IIS-hosted services, and additional details related to HTTP status codes and error handling and we could spend significant time on security models for REST. In the meantime, the code samples for this article should help you on your way!

Download the samples for this article at http://www.dasblonde.net/downloads/aspprojune08.zip.

 

Additional Resources

WCF Home: http://netfx3.com/content/WCFHome.aspx

Learning WCF (O Reilly 2007): http://www.thatindigogirl.com (get all the book code here!)

Michele s blog: http://www.dasblonde.net

IDesign: http://www.idesign.net

 

 

 

Hide comments

Comments

  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Publish