What is REST?
A REST web service is a service that embraces “Representational State Transfer”. What does this mean? Lets take a gander at the Wikipedia entry:
…
REST strictly refers to a collection of network architecture principles which outline how resources are defined and addressed. The term is often used in a looser sense to describe any simple interface which transmits domain-specific data over HTTP without an additional messaging layer such as SOAP or session tracking via HTTP cookies.
… (Source)
A simple ScreenCast illustrates this the best:
A Simple Example of REST Services
If I were using a REST service I would go to http://www.example.com/GetGasPrice/For/Zipcode/55402/ and the response would be a small POX (Plain old Xml) chunk that represents the price of gas at ZipCode 55402. Replace the “55402” with “96032” and you’ll get the gas price for zipcode 96032.
Here is what we would see on the screen:
Plain Ol’ Xml is being returned for our query. I can use .NET, PHP, C++, Ruby, Java, you name it, and we could parse this and move on with our application. REST is very interoperable.
This type of URL Scheming is very easy for users to understand as its following a Representational format. We’re representing what we want to do in the form of the Uri. It’s simple and easy to read. I can even access it via my web browser! We can change the zipcode from 55402 to 55401 and get a different price behind the scenes. Awesome.
However, this is not the only benefit of REST. There are many benefits of REST which are well beyond the scope of this introductory article. To learn more, read the wikipedia entry. In this article We’re going to cover HOW to implement a rest service in WCF 3.5.
How To Implement REST Services
Note: If you want to go straight to the code, scroll down a bit. The full code project is zipped up and also available for download as well at the bottom of the post.
The Web Programming Model
We will cover the most common and basic use of a REST service, the GET. However, we will glance over the other topics as well.
WCF Provides a REST Programming model for us to use . The following classes are of great intestest when programming REST services.
- WebGetAttribute – Handling the GET HTTP Verb
- WebInvokeAttribute – Handling the other HTTP Verbs (POST, DELETE, PUT). This will not be covered today. Part 2 or 3 will cover this.
- WebHttpBinding – The WCF binding that ties it together.
- WebServiceHost – An extension of the ServiceHost class for hosting REST Services.
WebGetAttribute
The WebGet attribute allows us to specify how we want to handle GET HTTP Verbs for processing the REST service. As the examples shows, we are performing a GET on the URL and we’re expecting some POX results back.
The code for that WCF Operation Contract looks like this:
[OperationContract] [WebGet (RequestFormat = WebMessageFormat.Xml, ResponseFormat = WebMessageFormat.Xml, BodyStyle = WebMessageBodyStyle.Bare, UriTemplate = "/GetGasPrice/For/ZipCode/{zipCode}" )] GasPriceData GetPriceData(string zipCode);
RequestFormat is the format the request is made in. the two options are XML and JSON. If you request from the browser your request will still be honored.
ResponseFormat is the format that will be returned. The two options are XML and JSON. Here we are specifying that we would like to have XML returned to us.
BodyStyle represents how we want the data returned. Do we want the bare XML or the full “wrapped” XML?
Bare:
<GasPriceData>
<GasStation>Super America</GasStation>
<Price>2.05</Price>
</GasPriceData>
Wrapped:
<GetPriceDataResponse>
<GetPriceDataResult>
<a:GasStation>Super America</a:GasStation>
<a:Price>2.05</a:Price>
</GetPriceDataResult>
</GetPriceDataResponse>
UriTemplate describes how we are going to handle the Uri mapping. In this case we are stating that from the BaseAddress we are going to have a url that looks like the following: /GetGasPrice/For/ZipCode/{zipCode}. This Uri is going to map to the method “GetPriceData(string zipCode)”. The varibale in the UriTemplate – “{zipCode}” maps to the parameter in the method which is also called “zipCode”. We have now abstracted our method call to utilize a Representational format of Uri.
If we wanted, we could change the UriTemplate to something like this: “/GetGasPrice/For/Some/Other/Long/Path/To/The/ZipCode/{zipCode}” and in the URL of your service you would access it like this:
– http://www.example.com/GetGasPrice/For/Some/Long/Path/To/The/ZipCode/55402
This would execute the GetPriceData method at this location.
WebHttpBinding – This is best put by the MSDN doc, so I’m going to quote them:
The WebHttpBinding incorporates support for XML, JSON, and raw binary data using the WebMessageEncodingBindingElement. It is composed of an HttpsTransportBindingElement, an HttpTransportBindingElement and a WebHttpSecurity object. The WebHttpBinding is designed to be used in conjunction with the WebHttpBehavior.
WebServiceHost – Another one that the MSDN doc covers better than I could:
WebServiceHost extends the ServiceHost to make it easier to host a non-SOAP Web-style service. If WebServiceHost finds no endpoints in the service description, it automatically creates a default endpoint at the service’s base address. When creating a default HTTP endpoint, the WebServiceHost also disables the HTTP Help page and the Web Services Description Language (WSDL) GET functionality so the metadata endpoint does not interfere with the default HTTP endpoint. WebServiceHost also ensures that all endpoints that use WebHttpBinding have the required WebHttpBehavior attached. Finally, WebServiceHost automatically configures the endpoint’s binding to work with the associated Internet Information Services (IIS) security settings when used in a secure virtual directory.
The Code
IGasPriceService.cs
using System.ServiceModel; using System.ServiceModel.Web; using Part1.GasService; namespace Part1.GasService { [ServiceContract] public interface IGasPriceService { [OperationContract] [WebGet (ResponseFormat = WebMessageFormat.Xml, BodyStyle = WebMessageBodyStyle.Bare, UriTemplate = "/GetGasPrice/For/ZipCode/{zipCode}" )] GasPriceData GetPriceData(string zipCode); [OperationContract] [WebGet (RequestFormat = WebMessageFormat.Xml, ResponseFormat = WebMessageFormat.Xml, BodyStyle = WebMessageBodyStyle.Bare, UriTemplate = "/GetGasPrice/For/City/{city}" )] GasPriceData GetPriceDataForCity(string city); } }
This code explained …
This is the Service Contract which implements two methods for our REST service. One that will get gas prices by city name and one that will get it by ZipCode.
GasPriceService.cs
using Part1.GasService; namespace Part1.GasService { public class GasPriceService : IGasPriceService { #region IGasPriceService Members public GasPriceData GetPriceData(string zipCode) { switch (zipCode) { case "00000": return new GasPriceData { GasStation = "None", Price = 0.00m }; case "55410": return new GasPriceData {GasStation = "Holiday", Price = 1.99m}; case "55404": return new GasPriceData {GasStation = "Costco", Price = 1.79m}; case "55412": return new GasPriceData {GasStation = "Sams Club", Price = 2.79m}; default: return new GasPriceData {GasStation = "Super America", Price = 2.05m}; } } public GasPriceData GetPriceDataForCity(string city) { switch (city.ToLower()) { case "minneapolis": return new GasPriceData { GasStation = "Super America", Price = 1.87m }; case "stpaul": return new GasPriceData { GasStation = "Holiday", Price = 1.99m }; case "edina": return new GasPriceData { GasStation = "Costco", Price = 1.79m }; case "bloomington": case "richfield": case "woodbury": return new GasPriceData { GasStation = "Sams Club", Price = 2.79m }; case "shakopee": return new GasPriceData { GasStation = "BP", Price = 4.39m }; default: return new GasPriceData { GasStation = "Super America", Price = 2.05m }; } } #endregion } }
This code explained …
This is a quick hack to get data back to the service. In a real world scenario this would connect to some data store which would return the data. You would also probably want to cache this data for a short period of time to allow for more throughput throughout your system.
GasPriceData.cs
using System.Runtime.Serialization; namespace Part1.GasService { [DataContract] public class GasPriceData { [DataMember] public string GasStation { get; set; } [DataMember] public decimal Price { get; set; } } }
This code explained …
This is the complex type that represents the Gas Price Data itself. Again, in a real service this would be implemented as a more robust object model. This is for example purposes only.
ExampleWebHost.cs
using System; using System.ServiceModel.Web; using Part1.GasService; namespace Part1 { public class ExampleWebHost { public static void Main() { using (var gasPriceServiceHost = new WebServiceHost(typeof (GasPriceService))) { gasPriceServiceHost.Open(); Console.WriteLine("The Base Address for the Gas Price Service is: {0}",gasPriceServiceHost.BaseAddresses[0].AbsoluteUri); Console.WriteLine("The service is now active. To terminate, press."); Console.ReadLine(); } } } }
This code explained …
This code self hosts the WCF service and waits for a user to terminate it in a console window.
app.config
This code explained …
We are merely setting a base address and telling WCF to use the WebHttpBinding. The WebServiceHost takes care of the remaining config GOO that we would normally have to deal with. (how nice).
Starting Up The Service
Make sure your service is set to start as a console app and start up a new instance. At this time you should be able to access the REST service from your local host by sending a command to it like this:
The base address is “http://localhost:7002/ and then the UriTemplate is appended to that, making the full REST URL.
Clicking Enter, you will get your XML results. ๐
Possibilities: Extending your Solution
As you’ve seen in the code, you can also implement extend your functionality in REST services by allowing users to find the gas price for a given city:
Conclusion
It’s fairly simple to implement a WCF REST Service. Simply use the WebGet attribute, the Web Service host and the binding and you’re off to the races. Next topic: Other HTTP Verbs with WCF Rest Services (Part 2).
Leave a Reply
You must be logged in to post a comment.