Tuesday, February 11, 2014

MVC Filters Part 2 - Exception Filters


Exception Filter - By Narendra Shrestha

It’s already been said before that exception filters comes to life only when an exception is thrown from other filters, action methods and action results. Like authorization filter, this filter also has its custom implementation and default implementation too. We don’t have to worry a lot when we are implementing our own exception filters because there is virtually no security risk as in case of authorization filter.

When implementing custom exception filter, we need to implement interface “IExceptionFilter” in our custom class. The skeletal code for this interface is as listed below

using System;

namespace System.Web.Mvc
{
    // Summary:
    //     Defines the methods that are required for an exception filter.
    public interface IExceptionFilter
    {
        // Summary:
        //     Called when an exception occurs.
        //
        // Parameters:
        //   filterContext:
        //     The filter context.
        void OnException(ExceptionContext filterContext);
    }
}

Listing 1:- “IExceptionFilter” interface skeletal code

When exception arises, “OnException” method in our custom class is invoked. This method accepts input parameter of type “ExceptionContext”. “ExceptionContext” is derived from class controller “ControllerContext”. As a result, we get access to various properties such as route data, http context, requests, controller specific properties etc.

Apart from the derived properties, “ExceptionContext” has its own useful properties such as:-

·         Result” of type “ActionResult” which is the result of the action method
·         ExceptionHandled” of type “bool” indicates if another filter has handled the exception
·         Exception” of type “Exception” which is the unhandled exception that was thrown

Our action method may include several exception filters. If error arises on behalf of any exception filter, we set “ExceptionHandled” property to true. Based on that value, we will prevent the re-handling of the error in other exception filters. If exception is handled by none, then ASP.NET framework will display the unpopular error page. “Result” property is used by exception filter to instruct MVC framework accordingly. Since “Result” property is of type “ActionResult”, we can instruct MVC framework in many ways such as rendering a view, redirecting to route or action etc. However, setting “Result” property to null simply means we have cancelled the request.

To explore exception filter, let us create our own exception filter class “MyExceptionAttribute” within “infrastructure” folder. This class implements the interface “IExceptionFilter”. Still I haven’t revealed a valuable bit of information. In order to qualify as a filter, any custom class should be derived from class “FilterAttribute”. “FilterAttribute” implements “IMvcFilter” which grants it an identity of the filter. “FilterAttribute” is derived from class “Attribute” making the class eligible enough to be used as .net attribute. We didn’t do so in authorization filter because we weren’t implementing “IAuthorizationFilter”. Instead we derived our authorization filter from class “AuthorizeAttribute” which already takes care of all these necessary steps.
The code for the “MyExceptionAttribute” class is listed as below:-
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace Filters.Infrastructure
{
    public class MyExceptionAttribute:FilterAttribute, IExceptionFilter
    {
        #region IExceptionFilter Members

        public void OnException(ExceptionContext filterContext)
        {
            if (!filterContext.ExceptionHandled && filterContext.Exception is UnAthorizedException)
            {
                filterContext.Result = new ViewResult { ViewName = "UnAuthorized" };
                filterContext.ExceptionHandled = true;
            }
        }

        #endregion
    }
}

Listing 2:- “MyExceptionAttribute” class

Inside “OnException” method, we checked if the exception has been handled by other existing exception filters. If the exception happens to be of type “UnAthorizedException”, then the result is set to be of “ViewResult” type. The name of the view is specified as “UnAuthorized” which is an existing view in the shared folder (refer filter part 1). “ExceptionHandled” property is set to true so that other exception filter can know whether the particular exception has been handled by other exception filters. It is most likely to be useful in any action implementing multiple exception filters.  For example let us assume that an exception was thrown and has already been handled by an exception filter. When another exception filter that follows the first one refers to this property, the value will be already set to true. We are saved from overload i.e. unnecessary re-handling of the same error.
Take a close notice of class “UnAthorizedException” which happens to be a custom exception type. We place this custom exception type within “infrastructure” folder along with other custom filter codes. The following list shows the code for “UnAthorizedException”. Frankly, nothing has been done within this exception type rather than calling the base class methods. Still this fulfills our desire to own a custom exception type. Hence, we are planning to throw this exception in case of unauthorized access.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace Filters.Infrastructure
{
    public class UnAthorizedException:Exception
    {
        public UnAthorizedException()
        {
       
        }
        public UnAthorizedException(string message):base(message)
        {
       
        }
        public UnAthorizedException(string message, Exception innerException)
            : base(message, innerException)
        {
       
        }
    }
}

Listing 3:- “UnAthorizedException” class

The custom authorization feature should be cross checked or disabled before proceeding with example below. Since Authorization filters run prior to others, “FormsAuthentication.Authenticate” may behave oddly to your surprise. You will be reusing this authentication method again as mentioned below in the code. Below are the changes which are made within the “Home” controller.

[MyException]
public string Vault()
{
    //if (!Roles.RoleExists("admin"))
    //{
    //    Roles.CreateRole("admin");
    //    Roles.AddUsersToRole(new[] { "name" }, "admin");
    //}
    //FormsAuthentication.Authenticate("name", "name");
    //FormsAuthentication.SetAuthCookie("name", false);
    if (User.IsInRole("admin") && User.Identity.IsAuthenticated)
    {
        return "Welcome to the dummy vault";
    }
    else
    {
        throw new UnAthorizedException();
    }
}

Listing 4:-“Vault” action in “Home” controller

Take a closer look; the exception filter attribute “[MyException]” is applied to our “Vault” action. Whenever, an exception is thrown from our action method, the custom exception filter will be triggered. In case if the error is of custom exception “UnAthorizedException” type, exception filter will arrange suitable result.
Run the project, enter URL “/Home/Vault”, and you can see the following output. Although, role “admin” already exists (refer filter part 1), still we aren’t authenticated yet. As a result, error will be thrown for failed authentication and our custom exception filter will be triggered.


Figure 1:- When “UnAthorizedException” is thrown is thrown by custom exception filter

Uncomment the code in listing 4. Once we run the project and enter URL “/Home/Vault”, we will see the following output.


Figure 2:-When user is authorized, no exception will be generated by custom exception filter

Default Exception Filter

It’s a real fun to dig deep inside various nooks and corners of MVC and explore its functionality. It magnifies the scenarios which happen behind the scenes. Still we can’t count out the default functionality. One reason to embrace the default functionality is because we know that it’s strongly tested. Moreover, it has promising factors summing up for strong reliability. We can replicate everything in default implementation which we already achieved in the custom. Default implementation requires less effort i.e. less coding.
The default implementation of exception filter in MVC is “HandleErrorAttribute” (refer table 1 in Filter Part 1). This filter attribute implements the interface “IExceptionFilter”. It is also derived from “FilterAttribute”(necessary to be recognized as filter and to be readily be used as an attribute)
Like other filters, “HandleErrorAttribute” has its own properties as following:-
·         ExceptionType” of type “Type” indicating the exception typed to be handled. Its default value is “Exception
·         View” of type “string” indicates view to render when exception is thrown. The default view taken from the “Web.config” file.
·         Master” of type “string” is the layout to be used when rendering an exception view. When we don’t specify any layout, the default layout stated in “_ViewStart.cshtml” is chosen.

For default implementation, we are repeating the same thing that we already did in the custom implementation. We disabled all the codes related to custom exception filter from the “home” controller. The following code list the changes we made to “Vault” action method inside the “home” controller.
[HandleError(ExceptionType = typeof(UnAthorizedException), View = "UnAuthorized")]
public string Vault()
{
    //if (!Roles.RoleExists("admin"))
    //{
    //    Roles.CreateRole("admin");
    //    Roles.AddUsersToRole(new[] { "name" }, "admin");
    //}
    //FormsAuthentication.Authenticate("name", "name");
    //FormsAuthentication.SetAuthCookie("name", false);
    if (User.IsInRole("admin") && User.Identity.IsAuthenticated)
    {
        return "Welcome to the dummy vault";
    }
    else
    {
        throw new UnAthorizedException();
    }
}

Listing 5:- changes made to “Vault” action in “Home” controller for default exception filter

The default exception filter attribute “HandleError” is applied to the “Vault” action. As a named parameter, “ExceptionType” is set to be of type “UnAthorizedException”, and view in case of exception is set to “UnAuthorized”.

Also, we need to make little changes inside “Web.config” file. Since “HandleError” works only when custom error mode is turned on, in “Web.config” file we make following changes:-

<customErrors mode="On" defaultRedirect="/Views/Shared/Error.cshmtl"/>

Listing 6:-Changes to “Web.config” file to turn on “HandleError” attribute

If we run our project at this point, we can see the following output generated


Figure 3:- When “UnAthorizedException” is thrown is thrown by default exception filter

We aren’t authorized yet. As a result, exception of type “UnAthorizedException” is thrown, and we will be taken to this view as specified within the named parameter “View” for the “HandleErrorAttribute” filter attribute. When we uncomment the code in listing 5 and run the solution once again, we can see the following output.


Figure 4:-When user is authorized, no exception will be generated by default exception filter

Hence, we replicated same functionality in both default and custom exception filter. However, from the viewpoint of testability, the default one is much easier to mock and unit test. While working with custom one, we will need to mock many of HTTP context methods and properties. It’s not that hard to mock these classes, but still it depends on our choice. If you think you are better off without custom implementation, you may be right but still when you want your own custom behavior, the door is always open.

1 comment: