ASP.NET MVC Filters

理解Filter

Filter的应用场景

使用ASP.NET MVC框架进行开发时,是否恰当地使用Filter,是判断一个程序员逼格高低的一个重要因素。请求某个WEB页面或提交表单时,是否出现过那个经典的黄色错误页面?在调用某个返回JsonResult的Action时,是否返回了500错误?在判断用户是否登录或是否具有特定权限时,同样的代码是否在多个Action方法中到处都是?使用Filter,可以以更加优雅的方式来处理以上的问题。
Filter可以在处理Web请求时,以横切的方式在特定的环节注入一些逻辑,这些逻辑在一个地方进行编写,并且非常轻松地在多个地方进行调用。

四种基础类型的Filter

MVC框架提供了四种基础类型的Filter,如下表:

类型接口默认实现类描述
AuthorizationIAuthorizationFilterAuthorizeAttribute最先运行,先于其它Filter和Action方法
ExceptionIExceptionFilterHandleErrorAttribute当Action方法或其他Filter运行的过程中抛出异常时运行
ActionIActionFilterActionFilterAttribute在Action方法运行前或运行后执行
ResultIResultFilterActionFilterAttribute在Action方法返回前或返回后执行

将Filter应用到Controller或Action

将Filter应用到Controller或Action的方式很简单,下面的代码是把一个Filter应用到一个Action方法中

public class AdminController : Controller {
    [Authorize]
    public ViewResult Index() {
        // ...其它代码
    }
    [Authorize]
    public ViewResult Create() {
        // ...其它代码
    }    
}

将Filter应用到整个Controller

[Authorize]
public class AdminController : Controller { 
    public ViewResult Index() {
        // ...其它代码
    }
    public ViewResult Create() {
        // ...其它代码
    }    
}

使用Authorization Filter来验证用户是否登录

在处理URL请求时,Authorization Filter先与Action方法和其他的Filter运行,用来判断当前用户是否已经登录,如果未登录,则跳转到登录页面进行登录。我们可以通过实现IAuthorizationFilter接口来完成验证,也可以直接使用MVC框架内置的AuthorizeAttribute类来完成验证。但是,为了兼顾安全性和灵活性,本篇文章通过继承AuthorizeAttribute类并重写AuthorizeCore方法来完成。下面是个最简单的例子

public class CustomAuthAttribute : AuthorizeAttribute
{
    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        if (httpContext.Request.IsLocal || Session["username"] + "" != "")
        {
            return true;
        }
        else
        {
            return false;
        }
    }
}

将这个自定义的Authorization Filter应用于Controller

[CustomAuth]
public class AdminController : Controller { 
    public ViewResult Index() {
        // ...其它代码
    }
    public ViewResult Create() {
        // ...其它代码
    }    
}

使用Authorization Filter也可以用来做特定的权限验证,方法与自定义的Authorization Filter一样。

处理验证失败的返回结果

如果验证失败,可以设置一个跳转的路径。有时候,返回ViewResult和返回JsonResult的Action的返回方式还不一样,下面的代码演示了通过重写HandleUnauthorizedRequest方法来分别设置返回结果

protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
    UrlHelper urlHelper = new UrlHelper(filterContext.RequestContext);
    if (filterContext.HttpContext.Request.IsAjaxRequest())
    {
        filterContext.Result = new JsonResult()
        {
            //看具体情况来返回数据
            Data = new { Succeed = false, Message = "请刷新页面后重新登录"}
        };
    }
    else
    {
        filterContext.Result = new RedirectResult(
            urlHelper.Action("NeedLogin", "Authorize",
            new { ReturnUrl = filterContext.HttpContext.Request.Url.ToString() }));
    }
}

使用Exception Filter来处理异常

当Action方法抛出异常时,或者其它的Filter执行时抛出异常时,Exception Filter将会被执行。Exception Filter的主要目的有两个,一个是记录错误日志,另一个是重定向到一个友好的错误页面,而不是现实经典的黄色错误页面。

自定义一个Exception Filter

自定义一个Exception Filter需要实现IExceptionFilter接口,定义如下:

namespace System.Web.Mvc {
    public interface IExceptionFilter {
        void OnException(ExceptionContext filterContext);
    }
}

当异常发生时,OnException方法被调用,该方法的参数是一个ExceptionContext类型的对象,ExceptionContext类继承自ControllerContext类,我们可以直接使用ControllerContext类中的很多对象,如:Controller、HttpContext、RequestContext、IsChildAction、RouteData等。同时,ExceptionContext类还定义了以下属性供我们使用:

属性名类型描述
ActionDescriptorActionDescriptor提供抛出异常的Action方法的一些信息
ResultActionResult出现异常后,返回给用户的响应结果
ExceptionException被抛出的异常
ExceptionHandledbool该异常是否被处理过

如果一个Exception Filter处理了某个异常,就把ExceptionHandled属性设置为True。值得注意的是,当一个Action方法抛出异常时,跟这个Action关联的所有Exception Filter类都会被执行,无论ExceptionHandled是否为True。所以,建议在自定义一个Exception Filter类的时候,要首先判断一些ExceptionHandled属性是否为True。

在自定义的Exception Filter中,我们可以记录错误日志,并且指定一个返回页面。

public class CustomExceptionAttribute : FilterAttribute, IExceptionFilter
{
    public void OnException(ExceptionContext filterContext)
    {
        if (!filterContext.ExceptionHandled &&
        filterContext.Exception is ArgumentOutOfRangeException)
        {
            // log the error
            // ...
            // 指定返回结果
            filterContext.Result
            = new RedirectResult("~/Content/RangeErrorPage.html");
            filterContext.ExceptionHandled = true;
        }
    }
}

也可以指定一个View,并且想这个View传递一些数据

public class CustomExceptionAttribute : FilterAttribute, IExceptionFilter
{
    public void OnException(ExceptionContext filterContext)
    {
        if (!filterContext.ExceptionHandled &&
        filterContext.Exception is ArgumentOutOfRangeException)
        {
            // log the error
            // ...
            // 指定返回结果
            filterContext.Result = new ViewResult {
            ViewName = "CustomError",
            ViewData = new ViewDataDictionary<Object>(myObj)
            };
            filterContext.ExceptionHandled = true;
        }
    }
}

这里的名为CustomError的View,是一个强类型视图,类型为Object(可以指定为任何类型)
一定要注意,在CustomError这个View中使用动态数据时不要产生异常。

使用内置的Exception Filter来处理异常

上面演示了如何使用自定义的Exception Filter来处理异常,这在开发中用的不是太多,但有助于我们了解Exception Filter的工作方式。HandleErrorAttribute类是MVC框架中内置的用来处理异常的Filter,它的两个属性比较重要,一个是ExceptionType,类型为System.Type,它可以指定要处理的异常类型,如果不指定的话,默认为System.Exception类,即,处理所有异常类型。另一个比较重要的属性是View,类型为System.String,可以通过这个属性指定出现异常后返回的视图地址。默认为/Views/Shared/Error.cshtml 或 /Views/Shared/Error.cshtml。

[HandleError(ExceptionType = typeof(ArgumentOutOfRangeException), View = "CustomError")]
[Authorize]
public class AdminController : Controller {
    public ViewResult Index() {
        // ...其它代码
    }
    public ViewResult Create() {
        // ...其它代码
    }    
}

默认情况下,HandleErrorAttribute类并不能工作,需要激活一下,激活的方法是,在web.config文件中,System.Web节点下添加一个节点customErrors,将mode的属性设置为On

<system.Web>
    <customErrors mode="On" defaultRedirect="/Views/Shared/Error.cshtml"/>
</system.Web>

处理Action Filter和Result Filter

MVC框架为我们提供了ActionFilterAttribute类,定义如下:

public abstract class ActionFilterAttribute : FilterAttribute, IActionFilter, IResultFilter
{
    public virtual void OnActionExecuting(ActionExecutingContext filterContext)
    {
    }
    public virtual void OnActionExecuted(ActionExecutedContext filterContext)
    {
    }
    public virtual void OnResultExecuting(ResultExecutingContext filterContext)
    {
    }
    public virtual void OnResultExecuted(ResultExecutedContext filterContext)
    {
    }
}

OnActionExecuting方法在Action调用之前执行,OnActionExecuted方法在Action调用之后执行,OnResultExecuting在Action返回之前调用,OnResultExecuted在Action返回之后调用。我们可以定义一个类继承自ActionFilterAttribute ,然后重写某个方法。应用场景一般用于记录特定Action的活动日志。

Filter的其它特性

全局Filter

我们可以通过下面的方式注册一个全局的Filter,应用于所有的Controller和所有的Action。向App_Start/FilterConfig.cs中的RegisterGlobalFilters方法添加一个Filter对象

using System.Web;
using System.Web.Mvc;
using Filters.Infrastructure;
namespace Filters {
    public class FilterConfig {
        public static void RegisterGlobalFilters(GlobalFilterCollection filters) {
            filters.Add(new HandleErrorAttribute());
            //可以添加其它Filter对象
        }
    }
}

指定同类型的Filter类的执行顺序

如果为一个Action绑定了多个Action Filter,那个这些Filter的调用顺序是不确定的,我们可以指定这个顺序,通过设置Order属性为一个int值。Order属性定义在FilterAttribute类中,可以在所有的Filter中使用

[SimpleMessage(Message="A", Order=2)]
[SimpleMessage(Message="B", Order=1)]
public ActionResult Index() {
    Response.Write("Action method is running");
    return View();
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值