源栈培训: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


RazorPage

微软推荐的、最新的、基于Razor页面和.NET core的新一代Web项目开发技术,包括Razor Tag Helper、Model绑定和Validation、Session/Cookie、内置依赖注入等……

MVC

过去两年间最流行的、基于.NET Framework和MVC模式的ASP.NET MVC框架,主要用于讲解安全、性能、架构和各种实战功能演示……

其他Web项目

包括WebForm和WebApi

全部
关键字



帮助

反馈