zoukankan      html  css  js  c++  java
  • ASP.Net MVC探索之路 不想在多个Action上写同样的FitlerAttribute(上)

    (写完本文后,我去下载了ASP.NET MVC 3 RC,发现它对Filter的可控性方面进行了某些增强——不仅仅是针对全局Filter的
    GlobalFilterCollection——所以在此特别说明一下本文目前主要针对的是ASP.NET MVC 2.0 RTM,当然大部分都适用于3.0)

    以AuthorizeAttribute这个Filter举例,一个Controller有若干个Action,包括登录的Action(如Login)。这时我们有两种方式来实现:
    1、重新实现一个IAuthorizationFilter,在里面判断如果是Login这个Action,就不进行验证。然后将这个Filter作为FilterAttribute置于Controller定义上。或者Controller自身实现IAuthorizationFilter。
    2、除了Login这个Action之外的所有Action加上个Authorize。
    这两种方式虽然能够达到目的,但总觉得不够优雅。

    如果我想给所有Action注入一段html到页面底部,这种注入可能是临时的,我必须去修改Controller吗?(ASP.NET MVC 3 可以将Filter加入GlobalFilters集合中)
    如果我想动态控制某个Action允许由哪些角色访问,我通过修改Controller能实现吗?
    如果我想这时候控制的由哪些角色来访问,需求改变时我想要控制由哪些用户来访问呢?我还得去修改Controller吗?或者增加或修改Filter吗?

    在ASP.NET MVC 2 中,以上需求好像都需要重新编译Controller。

    这里整理一下我们的需求:能不能增删改Filter时不去修改Controller?能让所有Controller和Action定义处都干干净净的那就最好了。

    那就把Filter放在Controller外部来管理,在Action或ActionResult等执行Filter之前保证将需要的Filter准备好就行了。

    在解决问题之前,先简单回顾一下Action执行前后发生的事。

    我们知道,在ASP.NET MVC中,每一次请求通常都定位到一个具体的Controller的Action中。
    在默认情况下,由Action执行器(ControllerActionInvoker去控制Action的执行(或不执行),实际做事的是InvokeAction方法。

    InvokeAction方法首先去查找Action(由FindAction方法),如果Action被找到了,会通过反射的方式去检索该Action所属Controller的拥有的Filter以及Action拥有的Filter,如果Controller本身也实现了某些Filter接口,也会被检索到(由GetFilters方法)。将找到的所有Filter放入一个FilterInfo变量中(当然放入FilterInfo变量的Filter是经过排序和重复清理的),FilterInfo中保存的Filter不完全是Action自己的Attribute上定义的

    然后先执行找到的所有IAuthorizationFilter(由InvokeAuthorizationFilters方法)。在InvokeAuthorizationFilters方法中,只要有一个IAuthorizationFilter的ActionResult不为null就会立即返回到调用处,不会执行其他的IAuthorizationFilter了。InvokeAuthorizationFilters执行完成后,InvokeAction方法检测其执行结果,如果ActionResult不为null,则执行该Result,其他的IActionFilter,IActionResult就不管啦,否则InvokeAction方法继续。

    接着获取要传给Action的参数集(交给GetParameterValue方法),就执行InvokeActionMethodWithFilters方法,方法名已经足够说明它是干什么的了。

    如果一切正常,根据InvokeActionMethodWithFilters方法返回的结果去接着就执行InvokeActionResultWithFilters方法。

    在执行InvokeAuthorizationFilters一直到执行InvokeActionResultWithFilters的这一整个过程中如果发生异常,则根据捕获的异常执行InvokeExceptionFilters进行异常处理。

    代码
    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
    <stringobject> 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;
    }

    这里需要注意一点:InvokeAction、GetFilters、InvokeAuthorizationFilters、GetParameterValue、InvokeActionMethodWithFilters、InvokeActionResultWithFilters、InvokeExceptionFilters等全是虚方法,除非有足够的原因去继承IActionInvoker重写一个Action执行器,否则我觉得重写某些方法足够满足我们的扩展需求了。

    甚至ControllerActionInvoker类本身,在ASP.NET MVC基础架构中也是可以替换的,怎么替换呢?在继承Controller类实现我们自己的Controller时重写CreateActionInvoker方法就可以。

    另外还可以在构造Controller对象给它的ActionInvoker属性赋值,这又怎么赋值?重写DefaultControllerFactory创建Controller实例的GetControllerInstance方法。 然后在Applicaion_Start中设置新的ControllerFactor:
    ControllerBuilder.Current.SetControllerFactory(new YourControllerFactory());

    回到主题。 我们将Filter和Action的对应关系或Filter和Controller的对应关系存于一个集合中并缓存起来。在合适的位置“注入”进去就行了。

    从找到Action到执行InvokeAuthorizationFilters之前,必须将IAuthorizationFilter准备好;从找到Action到执行InvokeActionMethodWithFilters内部执行Action之前,必须将IActionFilter准备好;从找到Action到执行InvokeActionResultWithFilters内部执行ActionResult之前,必须将IResultFilter准备好。异常发生InvokeExceptionFilters执行之前,必须将IExceptionFilter准备好。 基于以上几点,我们好像可以在FindAction方法
    GetFilters方法、InvokeAuthorizationFilters方法、InvokeActionResultWithFilters方法和GetFilters方法内部,或InvokeExceptionFilters执行前将必要的Filter准备好就可以了。当然,最合适的莫过于GetFilters方法了。

    在GetFilters方法中,我们可以根据当前Action的特征(如方法名,或包括请求方式Get或Post等),在“Filter和Action对应表”进行检索;或者根据当前Controller的特征(类型、完整类名都可以),在"Filter和Controller对应表"中进行检索。将匹配的Filter累加入FilterInfo变量就可以了。

    感兴趣的可以去看看Oxite ,它实现了本章提到的一部分的需求。
  • 相关阅读:
    jquery实现选项卡(两句即可实现)
    常用特效积累
    jquery学习笔记
    idong常用js总结
    织梦添加幻灯片的方法
    LeetCode "Copy List with Random Pointer"
    LeetCode "Remove Nth Node From End of List"
    LeetCode "Sqrt(x)"
    LeetCode "Construct Binary Tree from Inorder and Postorder Traversal"
    LeetCode "Construct Binary Tree from Preorder and Inorder Traversal"
  • 原文地址:https://www.cnblogs.com/alby/p/1900555.html
Copyright © 2011-2022 走看看