web 2.0

MVC Tip - An easy way to deal with those NotFound Errors

My first introduction to MVC was the Nerd Dinner tutorial. From that tutorial I adopted the concept of redirecting the user to a “NotFound” page when a item was requested that does not exist. Here is an example of a controller action that returns the “NotFound” view:

public ActionResult Activate(int id)
{
    User user = _repository.FindById(id);
    if (user == null) return View("NotFound");
    //other code....
    return View("Activated");            
}

 

However, a problem may come into play when you try to encapsulate logic in a model class. For example, let's suppose I have a class named EditUserViewModel which loads the details for a user account. The constructor takes a repository and an integer value which represents the primary key used in the database.

public class EditUserFormViewModel
{
    public EditUserFormViewModel( IUserRepository repository, int id  ) : base()
    {            
        var user = repository.FindById(id);                
        //Load properties...
    }    
}

This is cool because the logic is now hidden inside the model class which has the desirable side effect of keeping the controller lightweight:

public ActionResult Edit(int id)
{
    var model = new EditUserFormViewModel(_repository, id);            
    return View(model);
}

The only issue now is that the code that retrieves the user from the repository is inside the model class. This means that I can no longer can easily check if the user is null and return the request to the NotFound page. So as a result, I came up with this simple solution. The first step is to create a new custom exception class called NotFoundException:

public class NotFoundException : Exception
{
    public NotFoundException() {}

    public NotFoundException( string message ) 
        : base( message )
    {            
    }

    public NotFoundException(string message, Exception innerException)
        : base(message, innerException)
    {
    }
}

Next, inside the EditUserViewModel, when a repository call returns a null object, I throw a NotFoundException:

public EditUserFormViewModel( IUserRepository repository, int id  ) : base()
{            
    var user = repository.FindById(id);
    if( user == null ) throw new NotFoundException();
    ...
}

Finally, since I am always use a base controller class, I can easily add a little bit of "centralized" code to streamline the process of handling all those potential NotFoundExceptions. I start by creating a new method named NotFound(). The NotFound() method has two purposes:

  1. It creates a strongly typed reference to the NotFound view which can be used in any of my controllers. So if I decide to return the NotFound view anywhere in my application I can call NotFound() instead of View("NotFound").
  2. I override the OnException in the base controller class so whenever a NotFoundException is thrown I automatically redirect the user my NotFound view by calling the NotFound() method.

Here is the code:

public class ApplicationController : Controller
{
    public ActionResult NotFound()
    {
        return View("NotFound");
    }

    protected override void OnException(ExceptionContext filterContext)
    {
        if (filterContext.Exception is NotFoundException)
        {
            filterContext.Result = NotFound();
            filterContext.ExceptionHandled = true;
            return;
        }

        base.OnException(filterContext);
    }

    ...
}

Anyway, I thought this was a great approach to handling NotFound errors. Hopefully you will find it useful too.

blog comments powered by Disqus