in

 

AgileJoe

Answering all world issues with, "...it depends..."

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!

Comments

 

Jeffrey Palermo said:

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

December 11, 2007 11:44 PM
 

Christopher Steen said:

Link Listing - December 11, 2007

December 12, 2007 12:22 AM
 

Joe Ocampo said:

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

December 12, 2007 1:40 AM
 

vincent said:

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.

January 27, 2008 12:44 AM
 

SC said:

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.

March 12, 2008 12:32 PM
 

ASP.Net MVC Framework, Give Me Rescues! « Ang3lFir3 - Life as a Code Poet said:

Pingback from  ASP.Net MVC Framework, Give Me Rescues! &laquo; Ang3lFir3 - Life as a Code Poet

March 12, 2008 2:48 PM
 

Joe Ocampo said:

@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!

March 14, 2008 8:06 AM

Leave a Comment

(required)  
(optional)
(required)  

Enter the numbers above:
Add
Copyright Los Techies 2007. All rights reserved.
Powered by Community Server (Commercial Edition), by Telligent Systems