Sunday, November 17, 2013

A review of Raygun.io - exception handling and notifications made easy


Error handling is often one of the last items on the backlog for the first release of a product. Or more to the point it often doesn't make it in at all until users start reporting unhandled exceptions and all of a sudden it becomes a management priority!

CBS where I work is predominantly a web development shop, up until now we've been using ELMAH to do our error handling. It's very easy to install and use and so far we haven't had any real complaints. Having said that I've been thinking for a while how nice it would be to have one portal where we could view any exceptions being thrown across our products and set up notifications for relevant team members. We are also doing more mobile and desktop application development and it would be nice to make use of a product that seamlessly spans all those environments.

Raygun from Mindscape looks promising so I thought I 'd dive in and take a look.

Setup
First you'll need to sign up for a Raygun account. Once you've done that you will be prompted to create an application for which you will be handing errors and subsequently given instructions on how to configure Raygun appropriately. This is a very slick and well though out process.

I then installed Raygun into my ASP.Net MVC project using the NuGet package manager, once again nice and easy. After that you need to decide how you want to capture exceptions - if you want to do it on a global level you can do by adding a configuration section to you web.config or you can do it manually  through handling exceptions in your code and using the Raygun client classes.

One nice touch is that when manually handling exceptions you can provide metadata with each exception. This is particularly useful if you want to add some context to your exceptions. Unfortunately I couldn't see an easy way to filter or search for this information through the web portal.

The Raygun Web Portal
This is where Raygun really needs to be adding value to the exception tracking life cycle. There are plenty of products that allow for the catching and storing of errors but what I'm looking for from Raygun is a single port of call where I can see all the exceptions my apps are throwing, easily analyse the history for each exception and set up the appropriate notifications. So does it measure up?

Firstly Raygun does provide the ability to set up multiple applications to be monitored - yay! I can see a timeline of exceptions being thrown, a dashboard summary of all exceptions along with some great summary information for each exception group. One of the most significant features of Raygun is that instances of an exceptions are grouped together - it's not exactly clear how this is done but I would think it's some sort of hash of where the exception occurred, exception type, message etc. This makes it very easy to deal with all the instances of a particular type of exception.



 

There is also the ability to assign a number of states to a particular exception grouping - I can see this being useful if there are a large number of exceptions being thrown and multiple team members with access to the portal. However some states, such as Resolved, are better off living in your Issue Tracking software, fortunately Raygun does provide integration with popular issue trackers such as JIRA, FogBugz etc through Plugins so that that new issues can be created for particular exception types.


When clicking on a particular exception grouping you get taken to the details screen for that particular  type of exception. This is probably my least favourite screen, it's dominated by a graph which doesn't have the ability to change the selected date range and hence has very little relevance unless you are dealing with a heap of exceptions that have occurred in the last 12 hours. There is also no easy way to see a list of all the instances of that exception - instead you have to make do with a previous and next button to work through them. In my opinion it would be better to get rid of the graph and show a table of the errors along with the date and time. Or at least the ability to toggle modes to switch between a table and the graph. Having said that the information it does capture about each  exception instance is exactly what you would expect, there is a full stack trace, environment information, request information etc.


The other feature of this screen is the ability to enter a comment against a particular type of exception. I think this is of fairly limited use for most professional software shops as they would most likely be tracking these exceptions in their Issue Tracking software.

Notifications
The notification system is very easy to use. When the system logs an exception you can set it to send you an email. If you like you can get a daily digest. Nice and simple and it works. I might be tempted to tweak it so that if no exceptions have been logged then you aren't sent a digest, the danger being that if you get too many empty digests you might begin to ignore them.

General User Interface
There are some parts of the UI that I find annoying. Culprit number one is the "Upgrade Now" banner that takes up the top 70px of each page! I know calls to action are important but this one sticks in the throat when it's dominating prime real estate and you can't hide it.

The second issue I have is with the amount of relevant content that one can easily see and how it is displayed. I found that there is far too much empty space not being used (particularly whitespace in the error details) and much more information could be conveyed with a bit of restyling - this is a tool for use by software professionals who are used to dealing with large amounts of data - give it to them! I also think an opportunity has been missed by not making it a Single Page Application. The web interface isn't exceptionally complicated and the full page refreshes when moving around feel slow and somewhat clunky.

In addition I would love to see exceptions being  shown in real time on the dashboard without refreshing the page (Web Sockets anyone?).

Pricing
So the million dollar question - what does it cost? Well the pricing's not bad - the 'Micro' package is USD$14/month for 1 application plus as many users as you want. However the real downside to this is that you only get 2 weeks worth of data retention - this could be a real issue if you have an intermittent exception that you're finding hard to reproduce and track down. It also means that you're not going to have any meaningful stats that can be used over an extended period of time. The 'Small' package at USD$39/month gives you 5 monitored apps and 30 days retention which is better. There are 2 larger packages which only really start making sense if you have a large number of applications that you want to be monitoring. So while the pricing isn't super expensive I think most people are going to opt for the Small Package.

Overall
I really like the functionality of Raygun, it's a product that I was crying out for before it came along. At the moment it does feel like a work in progress, while it has enough functionality to be useful I'm sure there's plenty more value that can be added to the exception management lifecycle.

So would I use it? Well that depends - if I could run enough applications through it to justify the Small or Medium packages then I probably would. If I was looking to support just one web based product then I might stick to ELMAH. If I was looking to capture errors from multiple sources like mobile, desktop apps and web applications then I think it's a much more compelling option.

All in all with some UI enhancements, extended dashboard functionality and more integration with other products I think Raygun  has a promising future ahead of it!



Saturday, July 14, 2012

How to build a grid with knockout - Part 3: Sorting and Styling

In Part 3 of this tutorial we're going to add the ability to sort the data in the grid by a particular column in ascending or descending order. The sorting is going to take place on the server and is going to work in conjunction with the paging that we implemented in Part 2. I've have also added some styling to the grid so the final result will look as follows:



Step 1 - The Javascript
Within our javascript view model I've added a method SetOrder() that can will be called when the user clicks on a column header, this looks as follows:

    function SetOrder(data, event, colName) {
        if (_this.colName === colName) {
            _this.switchSortOrder();
        } else {
            _this.colName = colName;
            _this.sortOrder = "asc";
        }
        setTHClass(event);
        loadDataFromServer();
    } 
 
     _this.SetOrder = SetOrder; 
 


The first thing the code does is check if we're already sorting by the column that has been clicked, if so we reverse the sort order. If this is the first time is has been clicked we set the sort order to ascending. This is a good example of how we can use the member properties of the view model, _this.colName and _this.sortOrder, to remember the state of the grid in between actions.

Once we've ascertained the column we're sorting by and the direction we can call the setTHClass() function to show the correct icon for the sorting column. The code for this looks as follows:

   function setTHClass(event) {
        //remove the existing classes
        $(event.currentTarget).parent().find(".sort_wrapper").children("span").removeClass("asc");
        $(event.currentTarget).parent().find(".sort_wrapper").children("span").removeClass("desc");
        if (_this.sortOrder === "asc") {
            $(event.currentTarget).children(".sort_wrapper").children("span").addClass("asc");
        } else {
            $(event.currentTarget).children(".sort_wrapper").children("span").addClass("desc");
        }
    }
 

 
We make use of css classes asc and desc to show the correct icon on the sorting column. The code first removes all traces of these classes from the columns and then adds the correct one where required. Extensive use of JQuery DOM traversal functions are used to make this task easier.

The other major change is in the loadDataFromServer() function where we now have to pass information to the server specifying how the data is to be sorted, this is done by adding the querystring parameters sidx and sord to the url that we post to the server:

function loadDataFromServer() {
            var url = '/people/data';
            //add paging params
            url += '?rows=' + _this.rowsPerPage() +
            '&page=' + _this.page() +
            //add sorting params     
                           '&sidx=' + _this.colName +          
                           '&sord=' + _this.sortOrder;
 
             $.post(
                url,
                function (data) {
                    _this.records(data.TotalRowsCount);
                    _this.totalPages(data.TotalPageCount);
 
                    var results = ko.observableArray();
                    _this.people.removeAll();
 
                    ko.mapping.fromJS(data.GridData, {}, results);
                    for (var i = 0; i < results().length; i++) {
                        _this.people.push(results()[i]);
                    };
                },
                'json'
            )
        }

Step 2 - The Controller
Now we need to modify the controller to make use of the additional querystring parameters that are being passed through. The first task is to sort our data by the requested column, this can be done by using a set of dynamic linq helpers that Scott Gu blogged about, it's all contained in one file that you will need to download and add to your project. You will also need to add a using clause of System.Linq.Dynamic to your controller class.

Once you have done that we can change our controller code to look as follows:

   public JsonResult Data(int rows, int page, string sidx, string sord)
        {
            IQueryable<Person> qryPeople = GetPersonQuery();

            GridDataResult gridDataResult = new GridDataResult();
            gridDataResult.TotalRowCount = qryPeople.Count();
 
            int totalPages = 1;
            if (rows > 0)
            {
                totalPages = gridDataResult.TotalRowCount / rows;
                if (gridDataResult.TotalRowCount % rows != 0)
                    totalPages += 1;
            }
 
            gridDataResult.TotalPageCount = totalPages;
 
            //now do the sorting
            qryPeople = qryPeople.OrderBy(sidx + " " + sord);
 
            gridDataResult.GridData = qryPeople
                .Skip((page - 1) * rows)
                .Take(rows);
 
            return Json(gridDataResult);
        }

Because we are now using the Dynamic Linq helpers we can order qryPeople using strings, perfect for doing sorting!

Step 3 - The Html
 The final step is modifying the html to make sure that when a column header is clicked the view model is notified to that we can reorder the grid. We do this by binding to the table header using knockout as follows:

<thead>
<tr>
    <th data-bind='click: function(data, event) { SetOrder(data, event, "FirstName")}'>
        <div class="sort_wrapper">First Name<span class="grid_order ui_icon"></span></div>
    </th>
    <th data-bind='click: function(data, event) { SetOrder(data, event, "LastName")}'>
        <div class="sort_wrapper">Last Name<span class="grid_order ui_icon"></span></div>
    </th>
    <th data-bind='click: function(data, event) { SetOrder(data, event, "Age")}'>
        <div class="sort_wrapper">Age<span class="grid_order ui_icon"></span></div>
    </th>
</tr>
</thead> 
 
We now have a grid that supports paging, sorting and has been nicely styled. If you would like to download the code you can do so here.

Friday, April 6, 2012

ASP.net MVC - Handling json dates with a custom converter.

Handling dates in json can be a real pain as it has no concept of a date datatype - however by using the jQuery ajax() method and specifying a custom converter you can easily turn dates sent from the server into Date objects on the client.

A date when it comes down from the server in json format looks like this:

"BirthDate":"\/Date(1507546800000)\/"

 What is nice about this rather unfriendly format is that is has the /Date prefix (the backslashes are escape chars in json), this means we can look at a string and determine whether we should treat is as a date or not.

I typically use the jQuery ajax() method ($.ajax) to get a json payload from the server. By default if you specify a dataType of 'json' the ajax() method will use an inbuilt converter to turn the returned json string into a javascript object. Fortunately this method also takes a converters object which we can use to parse and transform the json before it is passed to the success() method.

In order for our converter to be called we need to specify a particular datatype - this cannot be one of the default in-built ones or it will be ignored  (such as json) - in this instance I've called mine jsonWithDates. When you specify your converter the first option is a string which says what you're converter is going to take in and return. You need to provide two values in the string separated by a a space (this feels a bit clumsy!). In this case I've specified "text jsonWithDates" because I specified that the dataType I want to deal with is "jsonWithDates" and I want "text" passed into my converter. When the response is received from the server jQuery will look at the converters and see that I have a converter that knows how to do the necessary conversion. Be warned that if you specify "json jsonWithDates" your converter will be passed a javascript object to convert and not a json string as jQuery will do a conversion for you implicitly, that's why I've used "text jsonWithDates".

Ok - so now we know how to hook into the pipeline and set up a converter, that's the hard part done! The second option in the converter object is a function that takes in the payload from the response and provides a converted response. It's up to you now to handle the transformation as you see fit. For my purposes I've used the JSON.parse() method which handily takes in a function that we can use to map individual values when parsing json. In my function I do a check to see if it is a date using a regular expression, if it passes I convert it to a javascript date object and return it.
 
 
Now in my success method I will receive a javascript object that has properties that are of type Date. The full code looks like the following:
 
function LoadDataFromServer() {
        $.ajax({
            url: GetGridURL(),
            type: 'GET',
            dataType: 'jsonWithDates',
            contentType: 'application/json:charset=utf-8',
            //add a custom converter to handle dates and the way 
            //they are sent back from the
            //server in .net
            converters: {
                "text jsonWithDates":
                function (jsonText) {
                    return JSON.parse(jsonText, function (key, value) {
                        // Check for the /Date(x)/ pattern
                        var match = /\/Date\((\d+)\)\//.exec(value);
                        if (match) {
                            var dateIn = new Date(parseInt(value.substr(6)));
                            return dateIn;
                        }
                        // Not a date, so return the original value
                        return value;
                    });
                }
            },
            success:
                function (data) { alert(data);  }
        });
    }
} 
Json date conversion - done!

Saturday, March 24, 2012

How to build a grid with knockout - Part 2: Paging

In Part 1 of this tutorial we built a simple HTML table using knockout, in this post we're going to add paging functionality to it.

The code for the entire index.cshtml view now looks as follows:
@{
    ViewBag.Title = "Index";
}
 
<h2>Index</h2>
 
<table id="people">
    <thead>
    <tr>
        <th>First Name</th>
        <th>Last Name</th>
        <th>Age</th>
    </tr>
    </thead>
    <tbody data-bind="foreach: people">
        <tr>
            <td><span data-bind="text: FirstName"></span></td>
            <td><span data-bind="text: LastName"></span></td>
            <td><span data-bind="text: Age"></span></td>
        </tr>
    </tbody>
</table>
<div class='grid_footer ui-helper-clearfix'>
    <div class='grid_info'>
        <span data-bind='text: pagesText'></span>
    </div>
    <div class='paging'>
        <a data-bind='visible: page() > 1, click: PageFirst' class='ui-button'>First</a>
        <a data-bind='visible: page() > 1, click: PageBack' class='ui-button'>Previous</a>
        <a data-bind='visible: page() < totalPages(), click: PageNext' class='ui-button'>Next</a> 
        <a data-bind='visible: page() < totalPages(), click: PageLast' class='ui-button'>Last</a>
    </div>
</div>
 
 
<script type="text/javascript">
    function peopleViewModel() {
        var _this = {};
 
        //pager data
        _this.page = ko.observable(1);
        _this.records = ko.observable(1);
        _this.totalPages = ko.observable(1);
        _this.rowsPerPage = ko.observable(1);
        _this.pagesText = ko.computed(function () { return _this.page() + 
        " of " + _this.totalPages() + " pages"; });

        _this.people = ko.observableArray();
 
        /************************
        Public Functions
        ************************/
 
        function PageFirst(item) {
            _this.page(1);
            LoadDataFromServer();
        }
 
        _this.PageFirst = PageFirst;
 
        function PageLast(item) {
            _this.page(_this.totalPages());
            LoadDataFromServer();
        }
 
        _this.PageLast = PageLast;
 
 
        function PageBack(item) {
            _this.page(_this.page() - 1);
            LoadDataFromServer();
        }
 
        _this.PageBack = PageBack;
 
        function PageNext(item) {
            _this.page(_this.page() + 1);
            LoadDataFromServer();
        }
 
        _this.PageNext = PageNext;
 
        function LoadDataFromServer() {
            var url = '/people/data';
            //add paging params
            url += '?rows=' + _this.rowsPerPage() +
            '&page=' + _this.page();
 
            $.post(
                url,
                function (data) {
                    _this.records(data.TotalRowsCount);
                    _this.totalPages(data.TotalPageCount);
 
                    var results = ko.observableArray();
                    _this.people.removeAll();
 
                    ko.mapping.fromJS(data.GridData, {}, results);
                    for (var i = 0; i < results().length; i++) {
                        _this.people.push(results()[i]);
                    };
                },
                'json'
            )
        }
 
        /************************
        Initialization
        ************************/
 
        ko.applyBindings(_this, $("body").get(0));
        LoadDataFromServer();
 
        return _this;
    }
 
    var viewModel = peopleViewModel();
 
</script>


Step 1 - The HTML
If you look at the listing above you'll see I've added a <div> below the <table>, this is where the elements of the pager are going. Within the <div> I've added four <a>s for each of the paging buttons that will be required and a <span> to show some text. I've also introduced some new knockout binding methods: visible which determines if a span will be shown or not, and click which will call a function on our javascript model when the <a> is clicked.

Step 2 - The Javascript
Within our javascript view model I've added observables to track the information that we need to make the pager work. I've also added a pagesText variable which is a ko.computed() function. This means that when any of the observables that the function references are changed knockout will do a rebind, this is paricularly usefull in this instance.

The other major change is in the LoadDataFromServer() function where we now get an object back from the server that not only contains the rows for the grid but paging information including the total number of rows and the total number of pages.

I've set the number of rows per page in this instance to 1 so that the paging functionality is obvious with only 3 rows!

Step 3 - The Controller 
The code for the controller method now looks as follows:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
 
namespace knockoutGrid.Controllers
{
    public class PeopleController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }
 
 
        public JsonResult Data(int rows, int page)
        {
            List<Person> people = new List<Person>();
            people.Add(new Person("Richie""McCaw", 33));
            people.Add(new Person("Dan""Carter", 32));
            people.Add(new Person("Owen""Franks", 23));
 
            GridDataResult gridDataResult = new GridDataResult();
            gridDataResult.TotalRowCount = people.Count;
 
            int totalPages = 1;
            if (rows > 0)
            {
                totalPages = gridDataResult.TotalRowCount / rows;
                if (gridDataResult.TotalRowCount % rows != 0)
                    totalPages += 1;
            }
 
            gridDataResult.TotalPageCount = totalPages;
            gridDataResult.GridData = people.Skip((page - 1) * rows).Take(rows);
 
            return Json(gridDataResult);
        }
 
    }
 
 
    public class GridDataResult
    {
        public int TotalRowCount { getset; }
        public int TotalPageCount { getset; }
        public object GridData { getset; }
    }
 
    public class Person
    {
        public string FirstName { getset; }
        public string LastName { getset; }
        public int Age { getset; }
 
        public Person(string firstName, string lastName, int age)
        {
            this.FirstName = firstName;
            this.LastName = lastName;
            this.Age = age;
        }
    }
 
} 





I've added a class called GridDataResult - this will enable us to send some more information about the paging back to the browser, because I want to use this class for all my controller methods that return data to a grid I have made the GridData property of type object (you could use generics if you wanted to). The MVC JSON serializer has no problems with serializing this into JSON properly.

The GridData() controller method is still straight forward with some added logic to work out the total rows and pages for the grid. The LINQ methods skip() and next() are used to get the correct subset of the data to be sent back . If you are using a database framework that supports LINQ (such as the Entity Framework) to retrieve your records from a database this is particularly usefull.

And that's it! A grid with paging - done!

How to build a grid with knockout and ASP.Net MVC


Fairly recently the team at CBS decided to adopt knockout to help us build our UIs. One of our major undertakings was to replace the JQGrid with a knockout equivalent, we haven't regretted it for a minute. The ability to quickly adapt the functionality to meet the different demands of our various clients plus the ability to style easily and effectively (a big gripe with the JQGrid)  has paid off handsomely.

This tutorial is broken into 3 parts:
Part 1 - building the basic HTML table using knockout and loading data from the server
Part 2 - implementing paging
Part 3 - implementing sorting


In this tutorial I'm going to take you through building a basic html table using knockout and retrieving data from a controller method on the server.

Step 1 - Install the knockout and knockoutMapping js files
You can either download these from the knockout website or use NuGet - if you're using NuGet then you need to install the knockoutjs package and the knockout.mapping package this will add a couple of js files to the scripts folder in your solution.

Once you have the js files added you need to reference them, I normally add them to my _Layout.cshtml file as I use them on virtually every page in my applications. Your _layout.cshtml file should look as follows:

<!DOCTYPE html>
<html>
<head>
    <title>@ViewBag.Title</title>
    <link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />
    <script src="@Url.Content("~/Scripts/jquery-1.5.1.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/knockout.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/knockout.mapping-latest.js")" type="text/javascript"></script>
</head>
<body>
    <div class="page">
        <div id="header">
            <div id="title">
                <h1>My MVC Application</h1>
            </div>
            <div id="logindisplay">
                @Html.Partial("_LogOnPartial")
            </div>
            <div id="menucontainer">
                <ul id="menu">
                    <li>@Html.ActionLink("Home""Index""Home")</li>
                    <li>@Html.ActionLink("About""About""Home")</li>
                    <li>@Html.ActionLink("People""Index""People")</li>
                </ul>
            </div>
        </div>
        <div id="main">
            @RenderBody()
        </div>
        <div id="footer">
        </div>
    </div>
</body>
</html>
 
Step 2 - Build a ViewThe view I added for this demo is called Index.cshtml and lives in a Views/People folder. The code looks as follows:

@{
    ViewBag.Title = "Index";
}
 
<h2>Index</h2>
 
<table id="people">
    <thead>
    <tr>
        <th>First Name</th>
        <th>Last Name</th>
        <th>Age</th>
    </tr>
    </thead>
    <tbody data-bind="foreach: people">
        <tr>
            <td><span data-bind="text: FirstName"></span></td>
            <td><span data-bind="text: LastName"></span></td>
            <td><span data-bind="text: Age"></span></td>
        </tr>
    </tbody>
    
</table>
 
<script type="text/javascript">
    function peopleViewModel() {
        var _this = {};
 
        _this.people = ko.observableArray();
        ko.applyBindings(_this, $("#people").get(0));
 
        function LoadPeopleFromServer() {
            $.post(
                '/people/data',
                function (data) {
                    var results = ko.observableArray();
                    ko.mapping.fromJS(data, {}, results);
                    for (var i = 0; i < results().length; i++) {
                        _this.people.push(results()[i]);
                    };
                },
                'json'
            )
        }
 
        LoadPeopleFromServer();
 
        return _this;
    }
 
    var viewModel = peopleViewModel();
 
</script>
 
 
I've kept the HTML and Javascript in one file in order to make this easy to follow. If you have no experience of knockout I would strongly suggest you follow the excellent interactive tutorial on the knockout site. The HTML is vanilla knockout, it uses the data-bind attribute to specify how knockout binds values on the javascript model to DOM elements.

The javascript is more interesting: you'll notice that the peopleViewModel() object has a property _this.people which is an observableArray, this means that knockout will take notice when elements are added and removed from it and update the DOM accordingly. The LoadPeopleFromServer() method makes use of the jQuery ($.post) method to retrive data from the server in a JSON format, this JSON is returned to us as an array of javascript objects by JQuery, this is then taken by the knockout mapping utility and turned into an ko.observableArray of javascript objects. The beauty of using the mapping utlity is that we don't have to create any javascript objects to represent people, this is all done for us! This also has the advatage that if we add a new property to a person object on the server we don't need to change any javascript, only the HTML where we want the new value shown.

You may wonder why _this.people has been declared and we then push objects into it once they have returned from the server rather than just passing it into the mapping method. The reason for this is that the mapping method creates a new observableArray and assigs it to the variable you passed in. This ruins your knockout bindings as they were bound to the original array that you declared. You can get around this by only binding once you have got data back from the server but this gets more complicated once you introduce paging, etc. This pattern works and is easy to follow and implement.

Step 3 - The Controller Method
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
 
namespace knockoutGrid.Controllers
{
    public class PeopleController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }
 
 
        public JsonResult Data()
        {
            List<Person> people = new List<Person>();
            people.Add(new Person("Richie""McCaw", 33));
            people.Add(new Person("Dan""Carter", 32));
            people.Add(new Person("Owen""Franks", 23));
 
            return Json(people);
        }
 
    }
 
    public class Person
    {
        public string FirstName { getset; }
        public string LastName { getset; }
        public int Age { getset; }
 
        public Person(string firstName, string lastName, int age)
        {
            this.FirstName = firstName;
            this.LastName = lastName;
            this.Age = age;
        }
    }
 
}

The controller code is relatively simple, normally I would be retrieving data from a database but here I've hard coded it. The only things you really need to take heed of are the use of the JsonResult type that is being returned and the Json method that is needed to turn the list of people into valid JSON.

In Part 2 I will show you how to implement paging!

I have put the source code up on bitbucket, you can download it here.

Thursday, November 10, 2011

How to add offline functionality to your web pages using ASP.net MVC

I was recently tasked with creating an iPad client for our online survey product Prospero . My initial thoughts were that I was going to have to buy a Mac and a book on iPad programming, however after a bit of thought I realised that I could potentially use HTML5 and a combination of its offline caching and local storage capabilities.

The first issue that needs to be covered is geting the web browser to cache a version of a web page and all its required resources (images, scripts, etc) locally. Web browsers do this a lot of the time anyway in order to optimise performance, however in order for them to be used in an offline storage scenario they need to be stored in a separate cache. The way that you do this is by adding a manifest file to the page, this is done by adding a manifest attribute to the html tag.as follows:

<html manifest="/manifest.mf">


I choose to use an ashx (or web handler) to provide the manifest file so my code actually looks like the following:

<html manifest="/manifest.ashx"> 
 

  
The reason that I use a web handler is that you don't you need to make configuration changes to IIS in order to allow the manifest file extension to be associated with the correct content type. I like to keep IIS customisation to an absolute mimimum as it's just one more thing that can go wrong when deploying an app. The code for the web handler looks as follows:

using System;
using System.Web;
 
namespace Prospero
{
    public class Manifest : IHttpHandler
    {
        public void ProcessRequest(HttpContext context)
        {
            context.Response.ContentType = "text/cache-manifest";
            context.Response.WriteFile(context.Server.MapPath("~/content/survey.mf"));
        }
 
        public bool IsReusable
        {
            get
            {
                return false;
            }
        }
    }
}
 
 

In this example I have a manifest file survey,mdf in the content directory of my application.

The next rather daunting task is to work out what exactly needs to be written in the out in the manifest file. I realised that this should be pretty easy to work out by opening the web page and checking what resources had been downloaded using Firebug . I then wondered if someone has written a tool to automate this process and a quick Google turned up Manifested  . This awesome little tool will analyse your web page and tell you exactly what should be in your manifest file - just cut and paste the results into your .mf file!

Now you're all set to go - all you need to do is browse to your web page and it will be cached locally. If you  use Firefox to develop in you will get a warning bar at the top of the page asking you for permission to cache data locally - say yes and you're done. Now if you re-visit the page you will not be making any internet call except a call to retrieve the manifest file (if you are still online), this is done to check if there is a newer version on the server.  The way the browser determins if the file is newer is by checking the revision number in the file - you can force the browser to download all newer versions of your cached files by incrementing the revision number.


If you would like to double check what Firefox has cached you can do so by typing about:cache in the browser URL bar, the Offline Cache Device section show what is stored.