Now MVC has rescues: Kind Of

I really missed using the [Rescue] attribute from MonoRail in the ASP MVC framework.  I missed it so much that I decided to create my own.  With the help of Phil Haack’s ConventionController and Frederick Norman’s post on ErrorHandlerAttribute it wasn’t that hard at all.  In fact in the end I have something that almost looks like the MonoRail controller syntax.  Mind you it is not using any Filters but I really see how easy it would be implement them given the time.

Anyway, the code!

So the first thing I did was create the actual Rescue attribute:

public class RescueAttribute : Attribute
{
    private readonly IList<Type> exceptionTypes = new List<Type>();
    private readonly string rescueView;
    private readonly Action<Type> preRescueAction;


    public RescueAttribute(string rescueView) : this(rescueView, new Exception().GetType())
    {
    }


    public RescueAttribute(string rescueView, Type exceptionType)
    {
        this.rescueView = rescueView;

        if (exceptionType != null)
            exceptionTypes.Add(exceptionType);
    }

    public RescueAttribute(string rescueView, Type exceptionType, Action<Type> preRescueAction)
    {
        this.rescueView = rescueView;
        this.preRescueAction = preRescueAction;

        if (exceptionType != null)
            exceptionTypes.Add(exceptionType);
    }

    public RescueAttribute(string rescueView, params Type[] exceptionType)
    {
        this.rescueView = rescueView;

        if (exceptionType != null)
        {
            foreach (Type type in exceptionType)
            {
                exceptionTypes.Add(type);
            }
        }
    }

    public IEnumerable ExceptionTypes
    {
        get { return exceptionTypes; }
    }

    public string View
    {
        get { return rescueView; }
    }


    public Action<Type> PreRescueAction
    {
        get { return preRescueAction; }
    }
}

Now I had to add a couple of methods to Phil’s ConventionController:

protected override bool OnError(string actionName, MethodInfo methodInfo, Exception exception)
{
    ArrayList attributes = GetRescueHandlerAttribute(methodInfo);

    foreach (RescueAttribute rescueAttribute in attributes)
    {
        foreach (Type exceptionType in rescueAttribute.ExceptionTypes)
        {
            if (exceptionType.IsAssignableFrom(exception.InnerException.GetType()))
            {
                if(rescueAttribute.PreRescueAction != null)
                {
                    rescueAttribute.PreRescueAction.Invoke(exceptionType);
                }

                if (!string.IsNullOrEmpty(rescueAttribute.View))
                    RenderView("~/Views/Rescue/" + rescueAttribute.View + ".aspx", exception);

                return true;
            }
        }
    }

    return false;
}

private ArrayList GetRescueHandlerAttribute(ICustomAttributeProvider methodInfo)
{
    ArrayList attributes = new ArrayList();

    attributes.AddRange(
        methodInfo.GetCustomAttributes(
            typeof (RescueAttribute),
            false));

    attributes.AddRange(
        GetType().GetCustomAttributes(
            typeof (RescueAttribute),
            true));

    return attributes;
}

The OnError is called when ever there is an Exception within an Action.  All I did was simply find all the Rescue Attributes on the controller class and iterate through all the registered exception types.  If it finds a registered exception type it looks for a PreAction delegate or lambda and invokes it (still working on this but I thought it was overkill).

Finally it Renders the rescue view in the Rescue folder.

image

That’s it!

Now my controller looks like this (check out the new hip name for the convention controller:

[Rescue("HelpMe")]
public class HomeController : SuperConventionController
{
    public void Index()
    {
        RenderView("Index");
    }

    public void About()
    {
        RenderView("About");
    }

    public void Error()
    {
        throw new NotImplementedException("Did you get this?");
    }
}

When I type in the following url: http://localhost:52634/Home/Error

It immediately redirects me to the Rescue page HelpMe.aspx

image

I don’t think this code is near production ready but it serves as a quick representation on how quickly and easily you can extend the MVC framework.

Happy coding!

Related Articles:

Post Footer automatically generated by Add Post Footer Plugin for wordpress.

About Joe Ocampo

My personal philosophy is simple: "Have a good strategy that sets the environment for success through the enablement of the whole. Be agile but with a mind towards pragmatism. Delegate to the best qualified individuals, but don’t be afraid to involve yourself in all parts of a job. Treat everyone with respect, humility, and with a genuine pursuit towards excellence." Respected business and technical leader with expertise in directing organization towards effective results driven outcomes. Proven ability to perform and communicate from both technical and business perspectives. Strong technical and business acumen developed through experience, education and training. Provides the ability to utilize technology, harness business intelligence and execute strategically by optimizing systems, tools and process. Passionate about building people, companies and software by containing cost, maximizing operational throughput and capitalize on revenue. Looks to leverage the strengths of individuals and grow the organization to their maximum potential by harnessing the power of their collective whole and deliver results. Co-Founder of LosTechies.com
This entry was posted in Architechture, ASP.MVC. Bookmark the permalink. Follow any comments here with the RSS feed for this post.

5 Responses to Now MVC has rescues: Kind Of

  1. Will you submit this to MvcContrib.org as a patch?

  2. Joe Ocampo says:

    @ Jeff, sure will once I get the test finished.

  3. vincent says:

    Nice coding. Except, if I’m not wrong, that this does not handle exceptions thrown in a view. For example, if I call an object in my viewdata that is null.

    Like this example, where the Languages object is not set

    < %= Html.Select("LanguageID",ViewData.Languages)%>

    Funny thing is, I of course debugged, and the code entered the OnError method and went down to the line where the rescue view is rendered:

    RenderView(“~/Views/Rescue/”…)

    Yet, the exception is still thrown, so I guess that’s because it’s that inline code that throws it.

    Just wanted to give you this input.

  4. SC says:

    Now that the second release is out, how would your code have to be modified? For example, there is not more virtual bool OnError.

    Thanks.

  5. Joe Ocampo says:

    @SC

    The work I did with the Rescue was committed to the MVCContrib project. I believe they are refactoring as we speak based on the latest release of the MVC extensions.

    Cheers!