Action的执行

异步Action的定义

两种异步Action方法的定义

  1. xxxAsync/xxxCompleted

这种形式的异步只能定义在实现了AsyncController的Controller中.针对Task的异步没有这个限制

可以将异步操作实现在X xxAsync 方法中,而将最终内容的响应实现在XxxCompleted 方法中

对于以XxxA synclXxxCompleted 形式定义的异步Action 方法来说, ASP.NET MVC 并不会以异步的方式来调用XxxAsync 方法,所以我们需要在该方法中自行实现异步。也就是说需要在XXXAsync中自己实现异步,比如新建Task.

  1. Task返回值的异步Action

Task.Factory.StartNew({}).ContinueWith()

AsyncManager

TimeOut 超时控制

Parameters 传递参数

Finish触发Begin方法执行完的动作

clip_image001

clip_image002

clip_image003

AsyncManager在构造函数中注册了OperationCounter的Completed事件也就是Finish方法.而Finish方法会触发自己的Finished事件

对于每次通过Increment 和Decrement 方法调用引起的计数数值的改变, OperationCounter对象都会检验当前计数数值是否为零,为零则表明所有的操作运行完毕,在这种情况下Completed 事件会被触发。值得一提的是,表明所有操作完成执行的标志是计数器的值等于零,而不是小于零,如果我们通过调用Increment 和Decrement 方法使计数器的值成为一个负数,注册的Completed 事件是不会被执行的。

  1. XxxCompleted 万法的执行

XxxCompleted方法会在XXXAsync结束之后自动调用.原理是

XxxAsynclX xxCompleted 形式定义的异步Action 是通过一个ReflectedAsyncActionDescriptor 对象来表示的,它在执行BeginExecute 方法的时候会注册Controller 对象的AsyncManager 的Finished 事件,让该事件触发的时候去执行Completed 方法。

  1. 超时

AsyncManager有一个Timeout的超时字段,表示超时时间.在Begin方法执行之后,如果在这个时间内,complete方法没有执行,就会超时.

AsyncTimeoutAttribute:ActionFilterAttribute: FilterAttribute, IActionFilter, IResultFilter

NoAsyncTimeoutAttribute他们会在OnActionExecuting方法中设置AsyncManager 的超时时间

clip_image004

clip_image005

Action方法的执行

MvcHandler 对请求的处理

MvcHandler 同时实现了IHttpHandler 和IHttpAsyncHandler 接口,所以它总是调用BeginProcessRequestlE ndProcessRequest 方法以异步的方式来处理请求。

Controller的执行

激活的Controller对象在MvcHandler 的BeginProcessRequest 方法中是按照这样的方式执行的:如果Contro l1er的类型实现了IAsyncController 接口,则调用BeginE xecuteÆndExecute 方法以异步的方式执行,否则Controller 是通过调用Execute 方法以同步方式执行的。

protected internal virtual IAsyncResult BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, object state)
    {

        IController controller;

        IControllerFactory factory;

        ProcessRequestInit(httpContext, out controller, out factory);

        IAsyncController asyncController = controller as IAsyncController;

        if (asyncController != null) {

            asyncController.BeginExecute(RequestContext, asyncCallback, asyncState);

        } else {

            controller.Execute(RequestContext);

        }

    }

默认情况下通过Visual Studio 的向导创建的Controller 类型是抽象类型Controller 的子类。它同时实现了IController 和IAsyncController 这两个接口,所以MvcHandler 总是以异步的方式来执行Controller 。

Actionlnvoker 的执行

包括Model 绑定与验证在内的整个Action 的执行是通过一个名为ActionInvoker 的组件来完成的,它同样具有同步和异步两个版本,

Controller中的代码:

clip_image006

ActionInvoker的获取机制:首先获取异步的,其次同步的,最后使用默认的异步的

clip_image007

ControllerDescriptor 的同步与异步

如果Controller 使用ControllerActioninvoker ,它所有的Action 总是以同步的方式来执行,但是当AsyncControllerActionInvoker 作为Controller 的ActionInvoker 时,却并不意味着总是以异步的方式来执行所有的Action 。

至于这两种类型的ActionInvoker 具体采用怎样的执行方式,涉及到两个描述对象,即用于描述Controller 和Action 的ControllerDescriptor和ActionDescriptor.

ASP.NET MVC 具有两个具体的ControllerDescriptor , ReflectedControllerDescriptor 和ReflectedAsyncControllerDescriptor,它们分别代表同步和异步版本的ControllerDescriptor。ReflectedControllerDescriptor 和ReflectedAsyncControllerDescriptor,并非是对实现了IController和IAsyncController的描述,他们的区别在于创建他们的ActionInvoker

clip_image008

ActionDescriptor 的执行

异步方式执行Action需要两个条件:

  1. 使用AsyncActionInvoker
  2. 定义为异步的Action

筛选器的执行

一种基于AOP (面向方面编程)的设计

在Action 方法执行前后,这些筛选器会自动执行

ASP.NETMVC 提供了AuthorizationF ilter 、ActionFilter 、ResultFilter和ExceptionFilter 这四种筛选器,它们对应着四个接口IAuthorizationFilter 、IActionF ilter、IResultFilter 和IExceptionFilter 。

Filter及其提供及其

  1. 自定义特性形式的Filter,会转换成类型为Filter的实例.Filter是自定义特性Filter的封装

Instance表示特性

Order表示执行顺序,越小优先级越高

Scope表示应用的范围

clip_image009

clip_image010

  1. FilterProvider
    public interface IFilterProvider
        {
    
            IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor);
    
        }
    
        public static class FilterProviders
        {
    
            static FilterProviders()
            {
    
                Providers = new FilterProviderCollection();
    
                Providers.Add(GlobalFilters.Filters);
    
                Providers.Add(new FilterAttributeFilterProvider());
    
                Providers.Add(new ControllerInstanceFilterProvider());
    
            }
    
            public static FilterProviderCollection Providers { get; private set; }
    
        }
  1. FilterAttribute 与FilterAttributeFilterProvider

抽象类型System. Web.Mvc.FilterAttribute 是所有筛选器特性的基类

public abstract class FilterAttribute : Attribute, IMvcFilter{

public bool AllowMultiple

public int Order

}

public interface IMvcFilter

{

bool AllowMultiple { get; }

int Order { get; }

}
分别用于描述Controller 和Action 的类型ControllerDescriptor 和ActionDescriptor 均实现了ICustomAttributeProvider 接口,可以调用相应的方法获取应用在对应的Controller 类型或者Action 方法上包括FilterAttribute 在内的所有特性.

实际上这两个类型提供了单独的方法GetFilterAttributes 获取FilterAttribute 特性列表

clip_image011

FilterAttributeFilterProvider有一个方法:

public virtual IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
    {

        ControllerBase controller = controllerContext.Controller;

        if (controller == null) {

            return Enumerable.Empty<Filter>();

        }

        var typeFilters = GetControllerAttributes(controllerContext, actionDescriptor)

        .Select(attr => new Filter(attr, FilterScope.Controller, null));

        var methodFilters = GetActionAttributes(controllerContext, actionDescriptor)

        .Select(attr => new Filter(attr, FilterScope.Action, null));

        return typeFilters.Concat(methodFilters).ToList();

    }

    protected virtual IEnumerable<FilterAttribute> GetActionAttributes(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
    {

        return actionDescriptor.GetFilterAttributes(_cacheAttributeInstances);

    }

    protected virtual IEnumerable<FilterAttribute> GetControllerAttributes(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
    {

        return actionDescriptor.ControllerDescriptor.GetFilterAttributes(_cacheAttributeInstances);

    }
  1. Controller 与ControllerlnstanceFi IterProvider

Controller 本身就是一个筛选器.

抽象类Controller 实现了IActionFilter、IAuthorizationFilter、IExceptionFilter 和IResultFilter 四个接口。

public class ControllerInstanceFilterProvider : IFilterProvider
    {

        public IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
        {

            if (controllerContext.Controller != null) {

                // Use FilterScope.First and Order of Int32.MinValue to ensure controller instance methods always run first

                yield return new Filter(controllerContext.Controller, FilterScope.First, Int32.MinValue);

            }

        }

    }
  1. GlobalFilterCollection

所谓全局Filter,就是不需要显式与某个Controller 或者Action 进行关联就会默认应用到定义在所有Controller 中的Action 方法上

GlobalFilters

public static class GlobalFilters
    {

        static GlobalFilters()
        {

            Filters = new GlobalFilterCollection();

        }

        public static GlobalFilterCollection Filters { get; private set; }

    }
FilterInfo

ASP.NET MVC 的四种筛选器最终被封装成相应的Filter 对象,但是它们执行的时机和方式是不同的,所以在执行之前需要根据被封装的筛选器类型对所有Filter 进行分组。具体来说,当Actionlnvoker 被调用的时候,它会利用静态类型FilterProviders 得到所有注册的

FilterProvider ,并利用它们根据当前ControllerContext 和描述目标Action 的ActionD escriptor对象得到所有的Filter 对象,然后根据其Instance 属性表示的筛选器类型将它们分组,得到一个FilterInfo对象

FilterInfo

public class FilterInfo
    {

        private List<IActionFilter> _actionFilters = new List<IActionFilter>();

        private List<IAuthorizationFilter> _authorizationFilters = new List<IAuthorizationFilter>();

        private List<IExceptionFilter> _exceptionFilters = new List<IExceptionFilter>();

        private List<IResultFilter> _resultFilters = new List<IResultFilter>();

        public FilterInfo()
        {

        }

        public FilterInfo(IEnumerable<Filter> filters)
        {

            // evaluate the 'filters' enumerable only once since the operation can be quite expensive

            var filterInstances = filters.Select(f => f.Instance).ToList();

            _actionFilters.AddRange(filterInstances.OfType<IActionFilter>());

            _authorizationFilters.AddRange(filterInstances.OfType<IAuthorizationFilter>());

            _exceptionFilters.AddRange(filterInstances.OfType<IExceptionFilter>());

            _resultFilters.AddRange(filterInstances.OfType<IResultFilter>());

        }

        public IList<IActionFilter> ActionFilters
        {

            get { return _actionFilters; }

        }

        public IList<IAuthorizationFilter> AuthorizationFilters
        {

            get { return _authorizationFilters; }

        }

        public IList<IExceptionFilter> ExceptionFilters
        {

            get { return _exceptionFilters; }

        }

        public IList<IResultFilter> ResultFilters
        {

            get { return _resultFilters; }

        }

    }

AuthorizationFilter

  1. AuthorizationFilter在Action执行之前执行.

AuthorizationFilter

public interface IAuthorizationFilter
{

    void OnAuthorization(AuthorizationContext filterContext);

}

AuthorizationContext

public class AuthorizationContext : ControllerContext
{

    public AuthorizationContext()
    {

    }

    public AuthorizationContext(ControllerContext controllerContext)

        : base(controllerContext)
    {

    }

    public AuthorizationContext(ControllerContext controllerContext, ActionDescriptor actionDescriptor)

        : base(controllerContext)
    {

        if (actionDescriptor == null) {

            throw new ArgumentNullException("actionDescriptor");

        }

        ActionDescriptor = actionDescriptor;

    }

    public virtual ActionDescriptor ActionDescriptor { get; set; }

    public ActionResult Result { get; set; }

}
按照Filter 对象Order 和Scope 属性决定的顺序执行所有AuthorizationFilter 的OnAuthorization 方法。

在所有的AuthorizationFilter 都执行完毕之后,如果AuthorizationContext 对象的Result属性表示的ActionR esult 不为Null ,整个Action 的执行将会终止,该Actio nResult 将直接被执行用于完成对当前请求的响应

  1. AuthorizeAtlribute

如果我们要求某个Action 只能被认证的用户访问,可以在Controller 类型或者Action 方法上应用具有System.Web.Mvc.AuthorizeAttribute 特性

AuthorizeAttribute

protected virtual bool AuthorizeCore(HttpContextBase httpContext)
    {

        if (httpContext == null) {

            throw new ArgumentNullException("httpContext");

        }

        IPrincipal user = httpContext.User;

        if (!user.Identity.IsAuthenticated) {

            return false;

        }

        if (_usersSplit.Length > 0 && !_usersSplit.Contains(user.Identity.Name, StringComparer.OrdinalIgnoreCase)) {

            return false;

        }

        if (_rolesSplit.Length > 0 && !_rolesSplit.Any(user.IsInRole)) {

            return false;

        }

        return true;

    }
  1. PrincipaIPermissionAttribute
  2. RequireHttpsAttribute

RequireHttpsAttribute

public virtual void OnAuthorization(AuthorizationContext filterContext)
    {

        if (filterContext == null) {

            throw new ArgumentNullException("filterContext");

        }

        if (!filterContext.HttpContext.Request.IsSecureConnection) {

            HandleNonHttpsRequest(filterContext);

        }

    }

    protected virtual void HandleNonHttpsRequest(AuthorizationContext filterContext)
    {

        // only redirect for GET requests, otherwise the browser might not propagate the verb and request

        // body correctly.

        if (!String.Equals(filterContext.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase)) {

            throw new InvalidOperationException(MvcResources.RequireHttpsAttribute_MustUseSsl);

        }

        // redirect to HTTPS version of page

        string url = "https://" + filterContext.HttpContext.Request.Url.Host + filterContext.HttpContext.Request.RawUrl;

        filterContext.Result = new RedirectResult(url);

    }
  1. ValidateInputAttribute

为了避免用户在请求中嵌入一些不合法的内容对网站进行恶意攻击(比如XSS 攻击),ASP.NET 需要对请求的输入进行验证.

表示Http请求的抽象类型HttpRequestBase 具有一个Validatelnput 方法用于验证请求的输入。实际上这个方法仅仅是在请求上作一下标记而己,在读取相应的请求输入时才根据这些标记决定是否需要进行相应的验证

所有Controller 的基类ControllerBase 具有如下一个布尔类型的属性ValidateRequest ,它表示是否需要对请求输入进行验证,在默认情况下该属性的默认值为True ,意味着针对请求输入的验证在默认情况下是开启的。当ActionInvoker 在完成了对所有AuthorizationF ilter 的执行之后,会根据该属性决定是否需要通过调用当前HttpRequest 的Validatelnput 方法进行

请求输入的验证。

也正是由于ActionInvoker 针对请求输入验证是在所有AuthorizationFilter 执行之后进行的,所以我们可以通过自定义AuthorizationFilter 的方式来设置当前Controller 的ValidateRequest属性进而开启或者关闭针对请求输入的验证。

ValidatelnputAttribute

public virtual void OnAuthorization(AuthorizationContext filterContext)

{

if (filterContext == null)

{

throw new ArgumentNullException("filterContext");

}

filterContext.Controller.ValidateRequest = EnableValidation;

}设置这个属性

  1. ValidateAntiForgeryTokenAttribute

用于解决CSRF 跨站请求伪造的网络攻击,CSRF利用了站点对认证用户的信任.

ValidateAntiForgeryTokenAttribute

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]

    public sealed class ValidateAntiForgeryTokenAttribute : FilterAttribute, IAuthorizationFilter
    {

        private string _salt;

        public ValidateAntiForgeryTokenAttribute()

            : this(AntiForgery.Validate)
        {

        }

        internal ValidateAntiForgeryTokenAttribute(Action validateAction)
        {

            Debug.Assert(validateAction != null);

            ValidateAction = validateAction;

        }

        public string Salt
        {

            get { return _salt; }

            set
            {

                if (!String.IsNullOrEmpty(value)) {

                    throw new NotSupportedException("The 'Salt' property is deprecated. To specify custom data to be embedded within the token, use the static AntiForgeryConfig.AdditionalDataProvider property.");

                }

                _salt = value;

            }

        }

        internal Action ValidateAction { get; private set; }

        public void OnAuthorization(AuthorizationContext filterContext)
        {

            if (filterContext == null) {

                throw new ArgumentNullException("filterContext");

            }

            ValidateAction();//最终是调用了AntiForgery.Validate静态方法

        }

    }

攻击实例

clip_image012

解决方法

HtmlHelper 具有三个AntiForgeryToken 方法(这里的方法是HtmlHelper 的实例方法,不是扩展方法)。当我们在一个View 中调用这些方法时,它们会为我们创建一个所谓"防伪令牌C Anti-Forgery Token)" 的字符串,并以此生成一个类型为hidden的<input>元素。除此之外,该方法的调用还会根据这个防伪令牌设置一个具有HttpOnly 标记的Cookie

HtmlHelper

public MvcHtmlString AntiForgeryToken()
    {

        return new MvcHtmlString(AntiForgery.GetHtml().ToString());

    }

    //AntiForgery

    public static HtmlString GetHtml()
    {

        if (HttpContext.Current == null) {

            throw new ArgumentException(WebPageResources.HttpContextUnavailable);

        }

        TagBuilder retVal = _worker.GetFormInputElement(new HttpContextWrapper(HttpContext.Current));

        return retVal.ToHtmlString(TagRenderMode.SelfClosing);

    }

    public static void Validate()
    {

        if (HttpContext.Current == null) {

            throw new ArgumentException(WebPageResources.HttpContextUnavailable);

        }

        _worker.Validate(new HttpContextWrapper(HttpContext.Current));

    }
  1. ChildActionOnlyAttribute

这个Action只能以子Action的 形式在某个View中被调用,对于非子Action 调用它会直接抛出一个InvalidOperationException 异常。

当我们在调用HtmlHelper 的扩展方法Action/RenderAction 的时候会将当前的ViewContext 作为"parentViewContext" 保存到表示

当前路由数据的RouteData 的DataTokens 属性中,对应的Key 为"ParentActionViewContext"。如下面的代码片段所示, ControllerContext 的IsChildAction 属性正是通过该路由信息来判断当前请求是否是针对子Action 的调用。

Html中有这样一行代码: routeData.DataTokens[ControllerContext.ParentActionViewContextToken] = parentViewContext;

ChildActionOnlyAttribute

public sealed class ChildActionOnlyAttribute : FilterAttribute, IAuthorizationFilter
    {

        public void OnAuthorization(AuthorizationContext filterContext)
        {

            if (filterContext == null) {

                throw new ArgumentNullException("filterContext");

            }

            if (!filterContext.IsChildAction) {

                throw Error.ChildActionOnlyAttribute_MustBeInChildRequest(filterContext.ActionDescriptor);

            }

        }

    }

    public virtual bool IsChildAction
    {

        get
        {

            RouteData routeData = RouteData;

            if (routeData == null) {

                return false;

            }

            return routeData.DataTokens.ContainsKey(ParentActionViewContextToken);

        }

    }

ActionFilter

IActionFilter

public interface IActionFilter

{

void OnActionExecuting(ActionExecutingContext filterContext);在Action执行前调用

void OnActionExecuted(ActionExecutedContext filterContext);执行后调用

}

ActionExecutingContext

public class ActionExecutingContext : ControllerContext

{

public ActionExecutingContext(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary<string, object> actionParameters)

public virtual ActionDescriptor ActionDescriptor { get; set; }

public virtual IDictionary<string, object> ActionParameters { get; set; }

public ActionResult Result { get; set; }

一旦Result 属性被成功赋值,将会终止后续ActionFilter 和最终目标方法的执行。

}

ActionExecutedContext

public class ActionExecutedContext : ControllerContext

{

private ActionResult _result;

public ActionExecutedContext()

{

}

public ActionExecutedContext(ControllerContext controllerContext, ActionDescriptor actionDescriptor, bool canceled, Exception exception)

: base(controllerContext)

{

if (actionDescriptor == null)

{

throw new ArgumentNullException("actionDescriptor");

}

ActionDescriptor = actionDescriptor;

Canceled = canceled;

Exception = exception;

}

public virtual ActionDescriptor ActionDescriptor { get; set; }

public virtual bool Canceled { get; set; }

public virtual Exception Exception { get; set; }

public bool ExceptionHandled { get; set; }

public ActionResult Result

{

get { return _result ?? EmptyResult.Instance; }

set { _result = value; }

}

}

  1. ActionFilter 的执行机制

在Action执行之前,根据Order和Scope属性对ActionFilter属性进行排序,依次调用OnActionExecuting方法.执行完所有的ing方法之后,按照相反的顺序调用OnActionExecuted方法

clip_image013

  1. ActionFilter 对ActionResult 的设置

在调用ActionF ilter 的OnActionExecuting 方法的过程中,一旦某个ActionFilter 为ActionExecutingContext 的Result 属性设置了一个ActionResult 对象,后续Actio nFilter 和目标Action 将不会被执行。此时ActionInvoker 会创建一个ActionExecutedContext

对象,设置的ActionResult 直接作为其Result 属性,而Cancel 属性被设置为True 。

clip_image014

  1. ActionFilter中的异常处理

如果ActionFilter 链的第一个ActionFilter 在执行OnActionExecuting 或者OnActionExecuted方法的过程中出现异常,那么这个异常会被直接抛出。如果抛出异常的并不是第一个ActionFilter,抛出异常会被捕捉。Actionlnvoker 会创建一个ActionExecuted Context 对象,抛出的异常直接作为它的Exception 属性。随后这个ActionExecutedContext 对象被作为参数调用前一个ActionFilter 的OnActionExecuted 方法,如果在执行过程中这个ActionFilter 将ActionExecutedContext 的ExceptionHandled 属性设置为True ,表明抛出的异常已经经过处理, 中的异常处理那么ActionInvoker 会按照正常的方式逆向调用后续ActionF ilter 链(整个ActionF ilter 链中位于当前Actio nF ilter 之前的部分)。

ExceptionFilter

ExceptionFilter 是一个用于进行异常处理的筛选器。ExceptionFilter 不仅仅用于处理执行整个ActionFilter 链(包括目标Action 方法的执行)最终抛出的异常,还用于处理执行整个ResultFilter 链(包括对Action 方法返回的ResultFilter 的执行〉最终抛出的异常。

IExceptionFilter

public interface IExceptionFilter

{

void OnException(ExceptionContext filterContext);

}

ExceptionContext

public class ExceptionContext : ControllerContext

{

private ActionResult _result;

public ExceptionContext()

{

}

public ExceptionContext(ControllerContext controllerContext, Exception exception)

: base(controllerContext)

{

if (exception == null)

{

throw new ArgumentNullException("exception");

}

Exception = exception;

}

public virtual Exception Exception { get; set; }

public bool ExceptionHandled { get; set; }

public ActionResult Result

{

get { return _result ?? EmptyResult.Instance; }

set { _result = value; }

}

}

三点需要着重强调

ExceptionFilter 链是反向执行的。对于根据Order 和Scope 属性排好序的ExceptionFilter

链,排在后面的具有更高的执行优先级。

将ExceptionContext 的ExceptionHandled 设置为True 并不能阻止后续ExceptionF ilter 的

执行。

如果ExceptionFilter 在执行OnException 过程中出现异常,整个ExceptionFilter 链的执

行将立即终止,并且该异常会被直接抛出来。

  1. HandleErrorAtlribute

HandleErrorAtlribute

public class HandleErrorAttribute : FilterAttribute, IExceptionFilter

{

private const string DefaultView = "Error";

private readonly object _typeId = new object();

private Type _exceptionType = typeof(Exception);

private string _master;

private string _view;

public Type ExceptionType 处理的异常类型

{

get { return _exceptionType; }

set

{

if (value == null)

{

throw new ArgumentNullException("value");

}

if (!typeof(Exception).IsAssignableFrom(value))

{

throw new ArgumentException(String.Format(CultureInfo.CurrentCulture,

MvcResources.ExceptionViewAttribute_NonExceptionType, value.FullName));

}

_exceptionType = value;

}

}

public string Master 错误页面使用的布局文件

{

get { return _master ?? String.Empty; }

set { _master = value; }

}

public override object TypeId

{

get { return _typeId; }

}

public string View错误页面

{

get { return (!String.IsNullOrEmpty(_view)) ? _view : DefaultView; }

set { _view = value; }

}

public virtual void OnException(ExceptionContext filterContext)

{

if (filterContext == null)

{

throw new ArgumentNullException("filterContext");

}

if (filterContext.IsChildAction)

{

return;

}

// If custom errors are disabled, we need to let the normal ASP.NET exception handler

// execute so that the user can see useful debugging information.

if (filterContext.ExceptionHandled || !filterContext.HttpContext.IsCustomErrorEnabled)

{

return;

}

Exception exception = filterContext.Exception;

// If this is not an HTTP 500 (for example, if somebody throws an HTTP 404 from an action method),

// ignore it.

if (new HttpException(null, exception).GetHttpCode() != 500)

{

return;

}

if (!ExceptionType.IsInstanceOfType(exception))

{

return;

}

string controllerName = (string)filterContext.RouteData.Values["controller"];

string actionName = (string)filterContext.RouteData.Values["action"];

HandleErrorInfo model = new HandleErrorInfo(filterContext.Exception, controllerName, actionName);

filterContext.Result = new ViewResult

{

ViewName = View,

MasterName = Master,

ViewData = new ViewDataDictionary<HandleErrorInfo>(model),

TempData = filterContext.Controller.TempData

};

filterContext.ExceptionHandled = true;

filterContext.HttpContext.Response.Clear();

filterContext.HttpContext.Response.StatusCode = 500;

filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;

}

}

ResultFilter

IResultFilter

public interface IResultFilter

{

void OnResultExecuting(ResultExecutingContext filterContext);在ActionResult对象的ExecuteResult方法前执行

void OnResultExecuted(ResultExecutedContext filterContext);

}

ResultExecutingContext

public class ResultExecutingContext : ControllerContext

{

public ResultExecutingContext()

{

}

public ResultExecutingContext(ControllerContext controllerContext, ActionResult result)

: base(controllerContext)

{

if (result == null)

{

throw new ArgumentNullException("result");

}

Result = result;

}

public bool Cancel { get; set; }

public virtual ActionResult Result { get; set; }

}

ResultExecutedContext

public class ResultExecutedContext : ControllerContext

{

// parameterless constructor used for mocking

public ResultExecutedContext()

{

}

public ResultExecutedContext(ControllerContext controllerContext, ActionResult result, bool canceled, Exception exception)

: base(controllerContext)

{

if (result == null)

{

throw new ArgumentNullException("result");

}

Result = result;

Canceled = canceled;

Exception = exception;

}

public virtual bool Canceled { get; set; }

public virtual Exception Exception { get; set; }

public bool ExceptionHandled { get; set; }

public virtual ActionResult Result { get; set; }

}

ControllerActionInvoker中

执行Action时,执行Filter的核心代码

public virtual bool InvokeAction(ControllerContext controllerContext, string actionName)
    {

        if (controllerContext == null) {

            throw new ArgumentNullException("controllerContext");

        }

        if (String.IsNullOrEmpty(actionName)) {

            throw new ArgumentException(MvcResources.Common_NullOrEmpty, "actionName");

        }

        ControllerDescriptor controllerDescriptor = GetControllerDescriptor(controllerContext);

        ActionDescriptor actionDescriptor = FindAction(controllerContext, controllerDescriptor, actionName);

        if (actionDescriptor != null) {

            FilterInfo filterInfo = GetFilters(controllerContext, actionDescriptor);

            try {

                AuthorizationContext authContext = InvokeAuthorizationFilters(controllerContext, filterInfo.AuthorizationFilters, actionDescriptor);

                if (authContext.Result != null) {

                    // the auth filter signaled that we should let it short-circuit the request

                    InvokeActionResult(controllerContext, authContext.Result);

                } else {

                    if (controllerContext.Controller.ValidateRequest) {

                        ValidateRequest(controllerContext);

                    }

                    IDictionary<string, object> parameters = GetParameterValues(controllerContext, actionDescriptor);

                    ActionExecutedContext postActionContext = InvokeActionMethodWithFilters(controllerContext, filterInfo.ActionFilters, actionDescriptor, parameters);

                    InvokeActionResultWithFilters(controllerContext, filterInfo.ResultFilters, postActionContext.Result);

                }

            } catch (ThreadAbortException) {

                // This type of exception occurs as a result of Response.Redirect(), but we special-case so that

                // the filters don't see this as an error.

                throw;

            } catch (Exception ex) {

                // something blew up, so execute the exception filters

                ExceptionContext exceptionContext = InvokeExceptionFilters(controllerContext, filterInfo.ExceptionFilters, ex);

                if (!exceptionContext.ExceptionHandled) {

                    throw;

                }

                InvokeActionResult(controllerContext, exceptionContext.Result);

            }

            return true;

        }

        // notify controller that no method matched

        return false;

    }
原文地址:https://www.cnblogs.com/zhangliming/p/4567314.html