In order to explore how REST services are created and
exist within Windows Azure, this section takes the Web service from the
previous section and makes it RESTful. But, before we dive into the
implementation details of this change, let’s first take a step back and
think about REST-specific design considerations.
REST Service Addressing
A common design practice
with REST services is to make the addressing (the manner in which target
resources are addressed) as intuitive as possible. The social
bookmarking site Delicious is a great example of this.
With Delicious, every bookmark
has one or more tags (think of tags as categories). Tags essentially
replace folders within Web browsers with categories. In relation to our
discussion, you can also group tags into a bundle, which basically
creates “tag clouds.” Access to tagged bookmarks is provided via REST
services. Table 1 shows a set of sample URLs that can be used to get back a list of bookmarks for Azure, SOA, and Azure+SOA, respectively.
What’s important about this
example is that we are able to search, create and update a large network
of data via REST without writing code. The HTTP GET method and the
appropriate URLs are all we need.
Returning to our Order
service, we first need to define an appropriate resource addressing
structure for the order data, as shown in Table 2.
Table 2. The resource addressing structure for the Order service.
Action | IOrderService Operation Name | URI Address Template | HTTP Method |
---|
get a list of all orders | GetOrders | ./orders | GET |
get an order given the order ID | GetOrderByOrderId | ./order/{id} | GET |
get a list of orders for a given customer | GetOrdersByCustomer | ./orders/{custName} | GET |
create an order | CreateOrder | ./orders | POST |
update an order | UpdateOrder | ./order/{id} | PUT |
delete an order | DeleteOrder | ./order/{id} | DELETE |
Creating a Windows Azure REST Service
We now need to carry out a series of steps to make this a REST service:
1. | Add a reference to System.ServiceModel.Web in the Contract project.
|
2. | Add HTTP attributes to the methods defined in the IOrderService interface.
|
3. | Update the WCF behavior.
|
4. | Update the OrderService.svc file by adding a Web factory reference.
|
The System.ServiceModel.Web namespace contains classes that make up the Web HTTP programming model.
For our purposes, we need to focus on the following:
WebGetAttribute (maps to an HTTP GET)
WebInvokeAttribute (maps to HTTP POST, PUT, and DELETE)
WebMessageFormat (defines the format of the response message)
For the GET method, we use the WebGet attribute. We then use the UriTemplate attribute to define the addressing structure from Table 2.
This is a manual process, which means that it’s easy to make mistakes.
It is therefore important to lay out the URI structure prior to working
with the code.
We also need to specify the {token} parameters. For example, if we were calling the GetOrderByOrderId
operation of the Web service via SOAP, we would just pass in the order
ID argument by calling the Web method. But with REST, everything is
through HTTP methods and URIs. The service consumer doesn’t call GetOrderByOrderId directly, but rather does the HTTP GET method on http://server/OrderService.svc/order/2, where “2” is the order ID value.
Next, we need to determine the response message format by setting ResponseFormat to return XML messages.
Here’s what IOrderService looks like now:
Example 1.
[ServiceContract] public interface IOrderService { [WebInvoke(Method="POST", UriTemplate="orders", ResponseFormat=WebMessageFormat.Xml)] [OperationContract] int CreateOrder(Order o); [WebInvoke(Method="PUT", UriTemplate="order/{id}", ResponseFormat=WebMessageFormat.Xml)] [OperationContract] void UpdateOrder(string id, Order o); [WebGet(UriTemplate="order/{id}", ResponseFormat=WebMessageFormat.Xml)] [OperationContract] Order GetOrderByOrderId(string id); [WebGet(UriTemplate="orders/{custName}", ResponseFormat=WebMessageFormat.Xml)] [OperationContract] List<Order> GetOrdersByCustomer(string custName); [WebGet(UriTemplate="orders", ResponseFormat=WebMessageFormat.Xml)] [OperationContract] List<Order> GetOrders(); [WebInvoke(Method="DELETE", UriTemplate="order/{id}", ResponseFormat=WebMessageFormat.Xml)] [OperationContract] void DeleteOrder(string id); }
|
We now need to update the WCF behavior in the Web.Config file by changing the endpoint binding to WebHttpBinding and the endpoint behavior to a Web behavior, as shown here:
Example 2.
<services> <servicebehaviorConfiguration= "ServiceDemo_WebRole.ServiceDemoBehavior" name="ServiceDemo_WebRole.OrderService"> <endpoint address="" binding="WebHttpBinding" contract="Contract.IOrderService" behaviorConfiguration="Web"> </endpoint> </service> </services> <behaviors> <endpointBehaviors> <behavior name="Web" /> </endpointBehaviors> <serviceBehaviors> <behavior name= "ServiceDemo_WebRole.ServiceDemoBehavior"> </serviceBehaviors> </behaviors>
|
Finally, we have to update the OrderService.svc file to include WebServiceHostFactory, as shown here:
Example 3.
<%@ ServiceHost Language="C#" Debug="true" Service="ServiceDemo_WebRole.OrderService" CodeBehind="OrderService.svc.cs" Factory="System.ServiceModel. Activation.WebServiceHostFactory" %>
|
WebServiceHostFactory provides instances of WebServiceHost
in managed hosting environments, where the host instance is created
dynamically in response to incoming messages. This is necessary because
the service is being hosted using a Web role in IIS.