Compiling examples for consuming the REST Endpoints for WCF Service using Agatha
- by REA_ANDREW
I recently made two contributions to the Agatha Project by Davy Brion over on Google Code, and one of the things I wanted to follow up with was a post showing examples and some, seemingly required tid bits. The contributions which I made where: To support StructureMap To include REST (JSON and XML) support for the service contract The examples which I have made, I want to format them so they fit in with the current format of examples over on Agatha and hopefully create and submit a third patch which will include these examples to help others who wish to use these additions. Whilst building these examples for both XML and JSON I have learnt a couple of things which I feel are not really well documented, but are extremely good practice and once known make perfect sense. I have chosen a real basic e-commerce context for my example Requests and Responses, and have also made use of the excellent tool AutoMapper, again on Google Code. Setting the scene I have followed the Pipes and Filters Pattern with the IQueryable interface on my Repository and exposed the following methods to query Products: IQueryable<Product> GetProducts();
IQueryable<Product> ByCategoryName(this IQueryable<Product> products, string categoryName)
Product ByProductCode(this IQueryable<Product> products, String productCode)
I have an interface for the IProductRepository but for the concrete implementation I have simply created a protected getter which populates a private List<Product> with 100 test products with random data. Another good reason for following an interface based approach is that it will demonstrate usage of my first contribution which is the StructureMap support. Finally the two Domain Objects I have made are Product and Category as shown below:
public class Product
{
public String ProductCode { get; set; }
public String Name { get; set; }
public Decimal Price { get; set; }
public Decimal Rrp { get; set; }
public Category Category { get; set; }
}
public class Category
{
public String Name { get; set; }
}
Requirements for the REST Support
One of the things which you will notice with Agatha is that you do not have to decorate your Request and Response objects with the WCF Service Model Attributes like DataContract, DataMember etc… Unfortunately from what I have seen, these are required if you want the same types to work with your REST endpoint. I have not tried but I assume the same result can be achieved by simply decorating the same classes with the Serializable Attribute. Without this the operation will fail.
Another surprising thing I have found is that it did not work until I used the following Attribute parameters:
Name
Namespace
e.g.
[DataContract(Name = "GetProductsRequest", Namespace = "AgathaRestExample.Service.Requests")]
public class GetProductsRequest : Request
{
}
Although I was surprised by this, things kind of explained themselves when I got round to figuring out the exact construct required for both the XML and the REST. One of the things which you already know and are then reminded of is that each of your Requests and Responses ultimately inherit from an abstract base class respectively. This information needs to be represented in a way native to the format being used. I have seen this in XML but I have not seen the format which is required for the JSON.
JSON Consumer Example
I have used JQuery to create the example and I simply want to make two requests to the server which as you will know with Agatha are transmitted inside an array to reduce the service calls. I have also used a tool called json2 which is again over at Google Code simply to convert my JSON expression into its string format for transmission. You will notice that I specify the type of Request I am using and the relevant Namespace it belongs to. Also notice that the second request has a parameter so each of these two object are representing an abstract Request and the parameters of the object describe it.
<script type="text/javascript">
var bodyContent = $.ajax({
url: "http://localhost:50348/service.svc/json/processjsonrequests",
global: false,
contentType: "application/json; charset=utf-8",
type: "POST",
processData: true,
data: JSON.stringify([
{ __type: "GetProductsRequest:AgathaRestExample.Service.Requests" },
{ __type: "GetProductsByCategoryRequest:AgathaRestExample.Service.Requests", CategoryName: "Category1" }
]),
dataType: "json",
success: function(msg) {
alert(msg);
}
}).responseText;
</script>
XML Consumer Example
For the XML Consumer example I have chosen to use a simple Console Application and make a WebRequest to the service using the XML as a request. I have made a crude static method which simply reads from an XML File, replaces some value with a parameter and returns the formatted XML. I say crude but it simply shows how XML Templates for each type of Request could be made and then have a wrapper utility in whatever language you use to combine the requests which are required. The following XML is the same Request array as shown above but simply in the XML Format.
<?xml version="1.0" encoding="utf-8" ?>
<ArrayOfRequest xmlns="http://schemas.datacontract.org/2004/07/Agatha.Common" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Request i:type="a:GetProductsRequest" xmlns:a="AgathaRestExample.Service.Requests"/>
<Request i:type="a:GetProductsByCategoryRequest" xmlns:a="AgathaRestExample.Service.Requests">
<a:CategoryName>{CategoryName}</a:CategoryName>
</Request>
</ArrayOfRequest>
It is funny because I remember submitting a question to StackOverflow asking whether there was a REST Client Generation tool similar to what Microsoft used for their RestStarterKit but which could be applied to existing services which have REST endpoints attached. I could not find any but this is now definitely something which I am going to build, as I think it is extremely useful to have but also it should not be too difficult based on the information I now know about the above. Finally I thought that the Strategy Pattern would lend itself really well to this type of thing so it can accommodate for different languages.
I think that is about it, I have included the code for the example Console app which I made below incase anyone wants to have a mooch at the code. As I said above I want to reformat these to fit in with the current examples over on the Agatha project, but also now thinking about it, make a Documentation Web method…{brain ticking} :-)
Cheers for now and here is the final bit of code:
static void Main(string[] args)
{
var request = WebRequest.Create("http://localhost:50348/service.svc/xml/processxmlrequests");
request.Method = "POST";
request.ContentType = "text/xml";
using(var writer = new StreamWriter(request.GetRequestStream()))
{
writer.WriteLine(GetExampleRequestsString("Category1"));
}
var response = request.GetResponse();
using(var reader = new StreamReader(response.GetResponseStream()))
{
Console.WriteLine(reader.ReadToEnd());
}
Console.ReadLine();
}
static string GetExampleRequestsString(string categoryName)
{
var data = File.ReadAllText(Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "ExampleRequests.xml"));
data = data.Replace("{CategoryName}", categoryName);
return data;
}
}