Since my last blogpost about Web API Glenn Block appeared on Hanselminutes so it seems to be hot stuff at the moment. Having already written part of this blogpost, as it was actually part of the last post before I decided to break it in half, it seems a good time to finish it and get it out there. So building on my last post I will also take a look at how Web API plays along with JQuery and the new JQuery templates.
A word on caching
First off one of the things I learned is that you need to care about being explicit in reguards to caching. Crome and Firefox see Ajax calls as being non-cachable, but IE sees it as any other request. So I ran into a scenario where IE failed to get data after I had visited the URL of the Resource directly – until I cleared the browser cache. I then chose to handle caching explicitly, and the problem went away. So in my case I chose to set the cache control header to NoCache so I always get fresh data – but pretty often I would probably cache for a certain amount of time, like illustrated in the code that is commented out. Even better could be to implement a conditional get, which is covered very well by Pablo Cibraro in this blogpost. Lastly setting cache=false in JQuery also does the trick, but that would not give me an excuse to show how it is done with Web API, so here you go :)
[WebGet(UriTemplate = "{id}")]
public HttpResponseMessage<List<Product>> Get(int id)
{
var items = _repository.FindProductsFromCategoryById(id);
var response = new HttpResponseMessage<List<Product>>(items);
result.Headers.CacheControl = new CacheControlHeaderValue() { NoCache = true};
//result.Headers.CacheControl = new CacheControlHeaderValue()
// {
// Public = true, MaxAge = new TimeSpan(0,0,1,0)
// };
return response;
}
Another new friend - JQuery templates
Most web developers who have worked with Ajax have probably tried building part of a UI by concatenating strings of html. It is hardly ever pretty, and with just a bit of complexity it quickly becomes painful. Thankfully JQuery now has its own templating plugin, which can be uses via Microsofts CDN here http://ajax.microsoft.com/ajax/jquery.templates/beta1/jquery.tmpl.min.js
I wont try and battle the folks at JQuery in writing documentation, as they have already done a good job. So I will just show a quick example. A very straight foreward template could look like the sample below. As you can see all you need to get started is a script tag with type text/html and then you can write you template using the ${} syntax as placeholders for values.
<script id="productTemplate" type="text/html">
<li>
<h3>${ Name } (${ Id })</h3>
<p>Price: ${ Price }</p>
</li>
</script>
In order to bind data to this template you can do a couple of things. You can either bind directly using one line of code, or you can create a cached template for reuse and bind that.
$("#productTemplate").tmpl(value).appendTo("#products");
or
$("#productTemplate").template("productList");
$.tmpl("productList", data).appendTo("#products");
Using the shorthand syntax in JQuery for grabbing JSON from a service we can then simply grab and bind the data as cleanly as this.
$("#productTemplate").template("productList");
$.getJSON(
"products/12",
function (data) {
$.each(data,
function (index, value) {
$.tmpl("productList", value).appendTo("#products");
}
);
}
);
Whats the word - eeh verb. For now it is Post
Going the other way and submitting data back to the resource, can be done using http post. So to align with the previous sample a simple method on the resource could be written like this sample shows. As you can see it nicely sets the HttpStatusCode and the location header.
[WebInvoke(UriTemplate = "", Method = "POST")]
public HttpResponseMessage<Product> Post(Product item)
{
_repository.Add(item);
var response = new HttpResponseMessage<Product>(item);
response.StatusCode = HttpStatusCode.Created;
response.Headers.Location = new Uri("Products/" + item.Id, UriKind.Relative);
return response;
}
Now we need a form with some data to be posted, and since I don’t want to bore you with the details let’s look at using a basic html form like this one.
<form>
<input type="hidden" id="Id" name="Id" value="10" />
<input type="text" id="Name" name="Name" />
<input type="text" id="Price" name="Price" />
<button type="submit">Here</button>
</form>
Oddly enough there is no shorthand for doing a post and getting back JSON in JQuery, but thankfully it is still only a few lines of code code. To be honest I often use this approach anyway, so I can have a function that handles any error that might occur. What we are doing is handeling the submit event, where we serialize the form and do an Ajax post instead of a regular post, which would reload the page. Web API knows the application/x-www-form-urlencoded format so it handles binding to the Product model object.
$("form").submit(function () {
$.ajax({
type: "POST",
url: "products",
data: $(this).serialize(),
success: function (data, textStatus, jqXHR) {
$("#productTemplate").tmpl(data).appendTo("#products");
},
error: function (xhr, status, error) {
alert(errorThrown);
},
dataType: 'json'
});
return false;
});
The application/x-www-form-urlencoded format is really as simple as "Id=12&Name=test&Price=10" – to do the same as the form does in the sample. So building the data payload manually is no problem either.
Looking to the future
An interesting alternative to using JQuery that is comming up is DataJS, which among other things enables you to use HTML5 features for doing caching and prefetching. For now it only works with OData, but according to the codeplex site, it is supposed to support OData AND JSON. There are signs that they will be tailored to fit together nicely, so I for one look forward to getting my hands dirty.