Getting familiar with REST and WCF
Published 7/7/2009 6:00 AM by Toran Billups 3 Comments
I first started developing POX services that would pass XML to the browser for AJAX functionality in my early .NET 1.x days. I liked the way I could simply hit the url of my specific httphandler and it would show the xml that represented the resource I was interested in. What I did not realize was in doing so, I was getting very familiar with a type of web service known as REST.
REST stands for Representational state transfer, and this type of web service enables you to leverage the HTTP protocol for CRUD like operations. The best part about REST is that if you are already doing web development and understand the way HTTP works, you know how to use REST. This approach is centered around one idea, a resource. And the concept is simple: you need to get at some type of resource found on the web to view it or modify it.
The best part about using REST if you are looking for a way to do CRUD like operations over HTTP, is that you do not need any fancy toolkit or technology to get up and running. The RESTful service was designed to be simple - yes someone actually thought simple was a good idea for a change.
So if you are using SOAP based web services today and do not need a complex security model, this might be a good alternative to help reduce complexity. Note* if you are passing around sensitive data that requires heavy security, I would stick with the WCF SOAP type service as REST was not explicitly built with this type of functionality in mind. Not to say that it is impossible, but the WCF SOAP web service model gives you all this functionality right out of the box.To get started, lets take a look at how we call a REST web service to get at some resource. In the example below, we want to get at a collection of products.
http://server/productservice.svc/products/
If you were to type this into your browser, the default HTTP verb is the GET. So you can see from the above that if we specify the type of resource (products in this example) using the GET verb with no other parameters, we would get in return a collection of product elements (this being the resource) in XML or JSON.
The next example would be used if we wanted to return a specific product, with id of 10 for example.
http://server/productservice.svc/products/10
Again, if we use the GET verb here the REST service understands that we want to return a specific resource, and as we gave an id this time, instead of a collection we will get back a single product with id of 10.
All this GET stuff is simple, but what if we want to add a new product? Or update an existing product? How can we do this over HTTP? We simply use a different verb. Again - think simple, so if we want to create a new product we would use the below url with the POST http verb.
http://server/productservice.svc/products/11
But in addition to the url above and the POST verb, we need to stream over the data that represents a valid product so the service can add it. If instead of creating a new product, you wanted to update a product you would use the PUT verb instead of POST.
And finally, if you wanted to delete that product with and id of 10, you would use the below url with the DELETE verb.
http://server/productservice.svc/products/10
Now, the above was only a simple introduction to REST and I wanted to keep it brief as this post is not just about what REST is, but how to actually implement a simple REST service for CRUD like operations using .NET 3.5 and WCF. I am sure that I did not do REST any justice here, so if you are interested to learn more checkout this great post by Rick Strahl.
To get started, I will need to assume that you know a little about how to setup a traditional WCF web service, but if not you can always download the source code for the sample project and hack on it until you get the idea. I hate to skip on the basics of the WCF platform, but as with any new technology it would deserve a post all its own.
The first project we are building will be a WCF REST web service that will include a method to return a collection of products, a method that returns a specific product, a method to update a specific product and finally a method to delete a specific product. The second project we will build out is a client application that calls these methods on the REST web service to show how easy this is to get up and running.
The first thing I needed to research was how to declare the REST methods in such a way that you could call them directly through the url in the browser. It starts very similar to the SOAP WCF implementation, with a service contract and one operation contract per method.
namespace Service.Interfaces
{
[ServiceContract()]
public interface IProductService
{
[OperationContract()]
[WebGet(UriTemplate = "/products/", BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Json)]
List GetProductCollection();
[OperationContract()]
[WebGet(UriTemplate = "/products/{id}", BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Json)]
Product GetProductById(string id);
[OperationContract()]
[WebInvoke(Method = "PUT", UriTemplate = "/products/{id}")]
void SaveProduct(string id, System.IO.Stream data);
[OperationContract()]
[WebInvoke(Method = "DELETE", UriTemplate = "/products/{id}")]
void DeleteProductByID(string id);
}
}
The changes that enable this service to be available over HTTP are the [WebGet] and [WebInvoke] attributes shown above. The WebGet is for GET type operations, where the verb expected is the GET. The WebInvoke allows you to specify a verb, so the POST,PUT,DELETE are available here. The first required information in the WebGet is the UriTemplate, and this is simply the url that will follow the svc service in your uri. I typically keep this consistent with the business entity I am working with, as I want the uri to be very self explanatory.
By default this will return XML, but as you can see this is something you can configure. I decided to return JSON as the XML had a mess of the usual namespace stuff and I did not want to do any XML parsing. Also I suppose I was trying to avoid some of the bloat that you can get with large collections via XML.
After we declare the service contract, we need to implement this interface using the normal WCF like approach. We simply create a concrete implementation of the web service so when a client calls this we actually do something.
namespace Service
{
public class ProductService : IProductService
{
private IProductRepository _Repository;
public ProductService() : this(new ProductRepository())
{
}
public ProductService(IProductRepository Repository)
{
_Repository = Repository;
}
public List GetProductCollection()
{
return _Repository.GetProductCollection();
}
public Entity.Product GetProductById(string id)
{
return _Repository.GetProductById(id);
}
public void DeleteProductByID(string id)
{
_Repository.DeleteProductByID(id);
}
public void SaveProduct(string id, System.IO.Stream data)
{
Product ProductObject = default(Product);
string ProductJSON = null;
using (StreamReader reader = new StreamReader(data)) {
ProductJSON = reader.ReadToEnd();
}
ProductObject = JSONHelper.ConvertJsonStringToObject(ProductJSON);
_Repository.SaveProduct(ProductObject);
}
}
}
The only thing that might look different is that in our save method, we need to read the stream that is coming, and call a helper method to convert the json data into a product object. In your normal WCF SOAP web service you could accept a type of product, but this is not much extra work considering we can avoid the complexity of SOAP.
Now this is the basic server side work needed, the only work missing is the configuration and the svc file to expose the REST endpoint. So first we create a simple svc, remove the code behind if you have any and ensure the markup is similar to the below.
<%@ ServiceHost Language="VB" debug="true" service="RESTWCFWebService.Library.Service.ProductService" Factory="System.ServiceModel.Activation.WebServiceHostFactory" %>
We only need to tell the svc where to get the concrete implementation of the service. I also included the factory as I could not get this to work without it for some odd reason. Showing my lack of research here I suppose.
Along with the above endpoint, we need to setup the configuration so when the user hits our service we spin up the correct binding, etc
The basic idea here is that we need a way to tell WCF to use HTTP instead of the usual SOAP web service. To do this, we declare a special binding type of webHttpBinding. In addition, our endpoint must point to the service contract we declared and finally we need to use the custom webHttpBinding.
The above is all we need to get the web service up and running. If you now compile this and run it, you could type the following url into your browser and get a collection of products returned.
http://localhost:63770/ProductService.svc/products/
Now that we have the REST web service up and running, we can create the client project to leverage these methods. I am using an MVC application in the sample, but you can obviously do the same type of thing with a webforms implementation.
The first method I want to look at in our client application will show how to call the WebGet without an id to get a collection of products. Then we will take the JSON we get back from this service and build a list of products to pass to our view to render the html table
public ActionResult Products()
{
List ProductList;
string JSONString = null;
HttpWebRequest request = WebRequest.Create("http://localhost:63770/ProductService.svc/products/");
request.Method = "GET";
HttpWebResponse response = request.GetResponse();
using (StreamReader reader = new StreamReader(response.GetResponseStream())) {
JSONString = reader.ReadToEnd();
}
ProductList = JSONHelper>.ConvertJsonStringToObject(JSONString);
return View(ProductList);
}
Notice that on the client side, I decided to provide an implementation of our business entity (ProductDTO) because I could get some type safety for all things done in the MVC application. This is optional, but as simple as it was to take the JSON and build the DTO I felt it was worth the effort.
The next method to get a specific product is very similar to the above. The only difference is that we include the id in the request.
public ActionResult ProductDetail(int id)
{
ProductDTO Product;
string JSONString = null;
HttpWebRequest request = WebRequest.Create("http://localhost:63770/ProductService.svc/products/" + id.ToString());
request.Method = "GET";
HttpWebResponse response = request.GetResponse();
using (StreamReader reader = new StreamReader(response.GetResponseStream())) {
JSONString = reader.ReadToEnd();
}
Product = JSONHelper.ConvertJsonStringToObject(JSONString);
return PartialView("ProductDetail", Product);
}
Next we need to update a product, but in addition to passing the url with an id like the above we need to build a stream and pass it along.
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult UpdateProduct(int id, FormCollection collection)
{
ProductDTO Product = new ProductDTO();
string JSONString = null;
Product.ID = id;
Product.ProductName = collection("ProductName");
Product.QuantityPerUnit = collection("QuantityPerUnit");
Product.UnitPrice = collection("UnitPrice");
Product.UnitsInStock = collection("UnitsInStock");
Product.UnitsOnOrder = collection("UnitsOnOrder");
Product.ReorderLevel = collection("ReorderLevel");
Product.Discontinued = collection("Discontinued");
JSONString = JSONHelper.ConvertObjectToJsonString(Product);
HttpWebRequest request = WebRequest.Create("http://localhost:63770/ProductService.svc/products/" + id.ToString());
request.Method = "PUT";
HttpWebResponse response = default(HttpWebResponse);
request.ContentType = "application/x-www-form-urlencoded";
System.Text.UTF8Encoding data = new System.Text.UTF8Encoding();
byte[] byteArray = data.GetBytes(JSONString);
request.ContentLength = byteArray.Length;
System.IO.Stream requestStream = request.GetRequestStream();
requestStream.Write(byteArray, 0, byteArray.Length);
requestStream.Close();
requestStream.Dispose();
request.AllowAutoRedirect = true;
response = (HttpWebResponse)request.GetResponse();
return this.Content("complete ", "text/xml");
}
I would abstract out a bulk of this code that is used to setup and stream the data, but I thought to leave it in the sample client project.
Now the above is all that we need to call the REST web service methods to do the usual CRUD operations. I hope this helped someone setup a REST implementation using WCF in .NET 3.5. If you want to investigate further, download the source here.
Hi, If you happen to have any free time and are still into this subject, I'm trying to debug a case where a REST service that accepts a stream argument is really not streaming. There is a buffer somewhere on the server side that accumulates the message up until the last byte is received and then forwards it to the service handler. This is not really noticeable unless unless using a slow upload or one that pauses. The example is very close to your example. http://stackoverflow.com/questions/6313851/httpwebrequest-upload-using-stream-always-buffering Thanks, Chris
I like XML (and don't know JSON well). Here's the parse routine if anyone needs it. (I got an error submitting the code so it's in the provided link.)
Oops, and then I ended up using Linq to XML which was vastly simpler than the code I posted (see my name link above). I still had trouble with getting null fields when using WebInvoke POST. But... the 4.0 framework has a DataContractSerializer class which is perfect for the job. I don't understand why it took so long for me to find that. Maybe because most the samples were written prior to 4.0 release.