源栈培训:ASP.NET MVC-3:Action Filter

更多
2019年08月04日 13点23分 作者:叶飞 修改

MVC使用Filter机制,使我们可以管理Action之前之后的流程。


四个接口

MVC首先定义了四个Fiter接口:

  1. IAuthorizationFilter:在Action运行之前被执行,一般用于处理权限验证相关的事宜
  2. IActionFilter:在Action运行时和运行完成时执行
  3. IResultFilter:在Result(通常就是View)运行时和运行完成时执行
  4. IExceptionFilter:在异常被抛出时执行

这四个接口在MVC管道中依次执行。


内置Filter

然后,MVC还为我们内置了一个FilterAttribute,下面再派生出若干子类,常用的有:

  • AuthorizeAttribute,实现IAuthorizationFilter
  • ActionFilterAttribute,实现IActionFilter和IResultFilter
  • HandleErrorAttribute,实现IExceptionFilter

通常我们只需要继承这些子类就可以进行开发了:

    public class ContextPerRequestAttribute : ActionFilterAttribute{}
    public class ErrorLogAttribute : HandleErrorAttribute{}
    public class NeedLogOnAttribute : AuthorizeAttribute{}

(演示:Filter的执行顺序

  1. AuthorizeAttribute.OnAuthorization()
  2. ActionFilterAttribute.OnActionExecuting()
  3. 运行Action()
  4. ActionFilterAttribute.OnActionExecuted()
  5. ActionFilterAttribute.OnResultExecuting()
  6. 运行View()
  7. ActionFilterAttribute.OnResultExecuted()
  8. HandleErrorAttribute

此外,Filter既可以放在Action上,也可以放在Controller上

    [ContextPerRequest(Order=100)]
    public class AdController : Controller{

        [NeedLogOn]
        public ActionResult Write(){}
    }

同一种类的Attribute,可以使用Order属性来设置执行顺序


何时释放Context?

可以利用Filter,实现ContextPerAction(近似于ContextPerRequest)模式。比如:

  1. 在OnActionExecuting()时实例化一个DbContext
  2. 但是,在什么时候提交DbContext中的变化,并销毁DbContext呢?
    • 如果View中绝对不会再使用DbContext,可以在OnActionExecuted()中实现
    • 如果View中还会使用到DbContext(通常是因为直接使用了BLL层的entity做ViewModel,且使用了LazyLoad机制),那就需要在OnResultExecuted()中实现

参数filterContext

提供了大量的“上下文信息”(context)供我们编程使用:

  • HttpContext:可以.出Request、Response、Session等
  • Controller:ViewData
  • ActionDescriptor:ActionName
  • RouteData:
还可以使用属性Result返回指定的页面:
            filterContext.Result = new RedirectResult("/Log/On"); 
(演示:略

另外,注意几个有用的方法/属性:

  • filterContext.HttpContext.Request.IsAjaxRequest():需要对Ajax请求特殊处理时非常有用
  • filterContext.ParentActionViewContext:ChildAction需要父Action信息时有用

(演示:根据Cookie判断当前用户,略)


自动验证

每个Post的Action里反复的写:

            if (!ModelState.IsValid)
            {
                return View(model);
            }
是不是很麻烦?有没有更简单的解决办法?
    //注意是继承自ActionFilterAttribute,不是Authorize
    public class AutoValidationAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            //ViewData:由View传递过来的Data
            ViewDataDictionary viewData = filterContext.Controller.ViewData;
            //只有当Action开始启动,才能获取ViewData
            if (!viewData.ModelState.IsValid)
            {
                filterContext.Result = new ViewResult
                {
                    //将ViewData传递给View(包含了验证失败信息)
                    ViewData = viewData,
                };
            }
            base.OnActionExecuting(filterContext);
        }
    }

然后在Action上使用

        [HttpPost]
        [AutoValidation]
        public ActionResult Index(IndexModel model){}
是不是这样就OK了?让城市列表的数据源来自Get Action……

(演示:略)

怎么办?

(再次复习Get Post Redirect

但问题在于:如何将错误消息传递给Get Action?


TempData

仅能用于Action之间传递的数据容器。

(演示:略

可以将ModelState放到TempData中,传递给Get Action,然后Get Action取出来使用。

但如何在Filter中实现呢?

            //存放
            context.Controller.TempData[Key] = context.Controller.ViewData.ModelState;

            //获取和Merge()
            var prevModelState = context.Controller.TempData[Key] as ModelStateDictionary;
            context.Controller.ViewData.ModelState.Merge(prevModelState);
            //转到相应的Get Action
            filterContext.Result = new RedirectToRouteResult(filterContext.RouteData.Values);//route data是否包含query string等?


总结:页面传值

页面之间其实不能传值(复习:HTTP无状态)。

所谓页面传值,指的是把在一个页面(后台)生成的值,传递给另外一个页面(后台)。

所有的“页面传值”都是通过“迂回”的方法:

  • cookie:数据传回客户端,再传到另一个页面
  • session:传送方式同cookie,但数据存放在服务器端
  • query string:数据通过url参数传递
  • hidden field:WebForm的ViewState实现机制,只有当被传值页面向服务器提交时有用
  • TempData:RazorPage和MVC内置,可由cookie(默认)和session实现
  • HttpContext.Items
  • Cache
  • ……


作业

  • 参考一起帮,使用[NeedLogOn]保证未登录用户无法访问只有登录用户才能访问的页面:
    1. 未登录用户强行访问上述页面,自动跳转到登录页面
    2. 此时的登录页面显示提示
    3. 登录之后,自动跳转到之前欲访问页面
  • 使用[AutoValidation]和[ImportModelError],自动完成Model Validation功能
  • 使用[ContextPerRequest],实现一个Request(注意:不是Action,ChildAction不使用新的DbContext)一个DbContext的功能
源栈培训 ASP.NET MVC
赞: 1 踩: 2

打赏
已收到打赏的 帮帮币

你的 打赏 非常重要!
为了保证文章的质量,每一篇文章的发布,都已经消耗了作者 1 枚 帮帮币
没有“帮帮币”,作者无法发布新的文章。

全系列阅读
评论 / 0
叶飞的系列文章

源栈培训:ASP.NET全栈开发

飞哥的源栈培训:线上全程直播,免费收看;线下拎包入住,按周计费。本系列收录所有讲义(含视频录播地址)

编程那些事:菜鸟入门

大飞哥倾力之作,面向有意入行IT/开发/编程的初学者,欢迎任何形式的留言建议……

从包工头到程序猿

真实故事,讲述我在家装公司关门之后,如何转行成为一个程序猿的故事。(《折腾》第三卷)

《折腾》(卷一)青涩

时间段:从大学毕业到开始创业。离开青葱校园,涉世之初的那些往事……

《折腾》(卷二)风雨 之(1)工地

我一个完全的门外汉(无论装修还是管理),开始给黎叔装修房子。从踌躅满志,到四处碰壁;从一往直前,到左右为难……

《折腾》(卷二)风雨 之(2)胸怀

作为一个律师,接工程没签合同,被狠狠的坑了一把!年轻人暴烈的想要复仇,黎叔教他一个企业家的胸怀……

《折腾》(卷二)风雨 之(3)渠道

成立了公司,招聘了员工,开始大力的拓展业务,一个接一个的坑,摔倒了又爬起来……

《折腾》(卷二)风雨 之(4)视野

经历残酷现实的磨砺,终于明白:干啥事,都不能闭门造车,人要走出去,开阔视野……

未分类

系统自动生成的未分类系列

一锅大杂烩

从律师到包工头,从码农到写手,读书交友生活创业,各种零零碎碎,乱七八糟……

人人都是程序猿

计算机编程普及课程,视频:https://space.bilibili.com/55410301/#/channel/detail?cid=49491

全部
关键字



帮助

反馈