January 2012 - Posts

Limited connectivity is one of the main challenges in web mobile. The stateless nature of http causes that  content  and associated static files like scripts or images be transmitted over the wire every time a page is fully refreshed (assuming http caching is not implemented correctly). Diverse techniques have emerged over the years to solve part of that problem by using the browser AJAX support. One of these techniques, which drastically changed the way we develop web applications is know as single page applications.

A single page application design assumes that good part of the website layout is fixed and there are a few placeholders for showing dynamic content. The layout and associated files can be download once, and the rest can be dynamically changed using AJAX calls to a backend Web API.    

There are also a few improvements that can be made like caching all the static content and associated files, and use client templates to remove all the overhead involved with downloading html markup on every AJAX call. By using client templates (e.g. JQuery templates, JSRender, Mustache) , only the data (usually JSON data) is negotiated with the backend, and the templates can be cached as static content as well.

In the area of web mobile, it is pretty important to optimize the use of the available bandwidth to support scenarios where the connectivity is very unreliable or limited data plans are used. This is exactly where single page applications make a lot of sense. As this kind of application makes a heavy use of javascript for DOM manipulation, it might not be ideal for all kind of mobile devices (the device should support DOM manipulation and XHR).

I personally think this kind of architecture for a web application will become a common norm now that we have devices with web support everywhere.

Posted by cibrax | 4 comment(s)
Filed under: ,

One common problem with the naming convention and default routing mechanism in ASP.NET MVC is that we tend to group actions in a controller for sharing an URL space.  This basically leads to complex controllers with a lot of unrelated methods that break the SOLID principles.  Too many responsibilities in a simple class affects maintainability in the long run and causes secondary effects that complicates unit testing. For example, we usually end up with class constructors that receives too many dependencies as it is discussed here in SO.

A service façade or a service locator are not the solution for this problem either. I personally think the solution here is to use controllers with fewer responsibilities and define the right routing in case you want to share the URL space. There are some discussions around the idea of using what was called controller-less actions, in which a controller only performs a single thing. Without going to that extreme, we can use a more resource oriented approach for assigning responsibilities to a controller.  A resource in http is uniquely identified by an URI  and can be manipulated through an unified interface with http verbs. Although some verbs do not make much sense when working with a browser like “delete” or “put”, we can replace those with “post”.

Implementing a delete action with an http get is definitely wrong, as an http get should be idempotent.

Let’s start an example by defining a simple scenario for managing a set of customers. We can initially define two resources, Customers for performing actions in the collection, and Customers/{id} for performing actions in a single customer. Although they both share the same URL space “Customers”, it does not mean we need to implement all the functionality in a single controller called “Customers”. We can still have a “CustomersController” for the collection, and a “CustomerController” for the individual customers. We can use routing for share the same URL and still forward the requests to the right controller.

We can define the following operations in the “CustomersController”,

  • Add (GET Customers/Add): Retrieves the Html form representation for creating a new customer
  • Add (POST Customers/Add): Receives a representation (encoded in the form) for creating a new customer in the collection
  • Delete (POST Customers/{id}/Delete: Removes a customer from the collection
  • Index (GET Customers): Retrieves a Html view representing a list of customers
public class CustomersController : Controller
{
    ICustomerRepository repository;
 
    public CustomersController()
        : this(new CustomerRepository())
    {
    }
 
    public CustomersController(ICustomerRepository repository)
    {
        this.repository = repository;
    }
 
    public ActionResult Index(string filter = null)
    {
        IEnumerable<Customer> customers = null;
 
        if (!string.IsNullOrEmpty(filter))
        {
            customers = this.repository.GetAll()
                .Where(c => c.FirstName.Contains(filter) || c.LastName.Contains(filter));
        }
        else
        {
            customers = this.repository.GetAll();
        }
        
        return View(customers);
    }
 
    [HttpGet]
    public ActionResult Add()
    {
        return View();
    }
 
    [HttpPost]
    public ActionResult Add(Customer customer)
    {
        if(ModelState.IsValid)
            this.repository.Add(customer);
 
        return RedirectToAction("Index");
    }
 
    [HttpPost]
    public ActionResult Delete(int id)
    {
        this.repository.Delete(id);
 
        if (Request.IsAjaxRequest())
            return new HttpStatusCodeResult((int)HttpStatusCode.OK);
 
        return RedirectToAction("Index");
    }
 
}

The “CustomerController” can have the following operations:

  • Get (GET Customers/{id}): Retrieves an Html form representing an specific customer
  • Update (POST Customers/{id}): Receives a representation (encoded in the form) for updating the customer
public class CustomerController : Controller
{
    ICustomerRepository repository;
 
    public CustomerController()
        : this(new CustomerRepository())
    {
    }
 
    public CustomerController(ICustomerRepository repository)
    {
        this.repository = repository;
    }
 
    [HttpGet]
    public ActionResult Get(int id)
    {
        var customer = this.repository.GetAll()
            .Where(c => c.Id == id)
            .FirstOrDefault();
 
        if (customer == null)
            return new HttpStatusCodeResult((int)HttpStatusCode.NotFound);
 
        return View(customer);
    }
 
    [HttpPost]
    public ActionResult Update(Customer customer)
    {
        if (ModelState.IsValid)
            this.repository.Update(customer);
 
        return RedirectToAction("Index", "Customers");
    }
 
}

This is how the routing table looks like,

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
 
    //This map is required so the Add segment is not used as {id}
    routes.MapRoute(
      name: "CustomerAdd",
      url: "Customers/Add",
      defaults: new { controller = "Customers", action = "Add" }
      );
 
    routes.MapRoute(
       name: "CustomerGet",
       url: "Customers/{id}",
       defaults: new { controller = "Customer", action = "Get" },
       constraints: new { httpMethod = new HttpMethodConstraint("GET") }
       );
 
    routes.MapRoute(
        name: "CustomerUpdate",
        url: "Customers/{id}",
        defaults: new { controller = "Customer", action = "Update" },
        constraints: new { httpMethod = new HttpMethodConstraint("POST") }
        );
 
    routes.MapRoute(
        name: "MVC Default",
        url: "{controller}/{action}/{id}",
        defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
    );
 
   
}

You can override the default route for “Customers/{id}” for getting or updating an specific customer by using an http method constraint. I also added the first  route for adding a new customer so the “Add” segment is not used as the “{id}” wildcard.

As you could see, we could split all the responsibilities in two controllers, which look simpler at first glance. In conclusion, you don’t need to assume the same URL means the same controller.

The “delete” action is implemented as an http post. This can be sent from the browser by using an Ajax call or a Http form. For example, the following code shows how to do that using a JQuery Ajax call.

@Html.ActionLink("Delete", "Delete", new { id = item.Id }, new { @class = "delete" })
<script type="text/javascript" language="javascript">
   1:  
   2:     $(function () {
   3:         $('.delete').click(function () {
   4:             var that = $(this);
   5:             var url = that.attr('href');
   6:  
   7:             $.post(url, function () {
   8:                 alert('delete called');
   9:             });
  10:  
  11:             return false;
  12:         });
  13:     });
</script>
The code is available for download from here
Posted by cibrax | 4 comment(s)
Filed under: , ,

Microsoft Patterns & Practices has recently started a new project whose codename is “Liike” (pronounced as LEEE-keh) for delivering guidance and a reference application in the mobile web space. As many of the recent initiatives started by different teams in Microsoft, this project will also be hosted in GitHub under http://liike.github.com/. All the artifacts, patterns and code generated during the project will be shared as part of that project.

Html5 and the rise of mobile devices have contributed to make Web Mobile one of the hot topics nowadays. Embarking in a new project for implementing a web mobile application can be a bit cumbersome without proper documentation or knowledge of proven practices. This project will try to address many of the concerns you might have by showing different patterns or solutions that can be applied in the context of web mobile, and how those solutions get reflected in real code with the Microsoft stack. The idea is not produce reusable code, but a reference application that people can explore.

As any of projects in MS Patterns & Practices, Microsoft does not have the final word on any of the discussed topics and a lot of feedback is received. They also have a team of external advisors with a bunch of talented people that have an extensive background working in this space.

Your help is always very valuable, so you can also contribute to the project by joining to the advisory team or giving feedback through user voice here.  

Posted by cibrax
Filed under: , , ,
More Posts