快速入门系列--MVC--05行为
Action执行包含内容比较多,主要有同步/异步Action的概念和执行过程,Authorationfilter, ActionFiltor, ResultFilter, ExceptionFilter等四个主要过滤器类型的执行过程。首先介绍异步的Action,之前学习Controller的时候已经知道默认情况下Controller的执行是异步的,在不继承异步Controller的情况,我们代码中的方法一般是同步的Action,我们可以通过使用Task<ActionResult>类型的返回值和在Action方法使用Task.Factory.StartNew()等方法来调用异步的Action,主要用于I/O绑定等操作。ASP.NET是通过线程池的机制来处理并发的HTTP的请求的,这种方式的优点是:工作线程的重用,减少线程的创建和释放;限制工作线程数量,避免高并发时服务器的崩溃。这里省略MVC4版本前的老式异步Action调用,Task返回值的Action如下所示:
public Task<ActionResult> Article(string name)
{
return Task.Factory.StartNew(() =>
{
string path = ControllerContext.HttpContext.Server.MapPath(string.Format(@"\articles\{0}.html", name));
using (StreamReader reader = new StreamReader(path))
{
AsyncManager.Parameters["content"] = reader.ReadToEnd();
}
}).ContinueWith<ActionResult>(task =>
{
string content = (string)AsyncManager.Parameters["content"];
return Content(content);
});
}
在上代码中,可以看到一个AsyncManager类,它起到了在异步操作和回调操作间传递参数的作用。这是一个关于异步操作很重要的类型,其属性OutstandingOperatons是一个异步操作计数器,类似信号量的概念,用Increment设置初始值,当一个或多个异步操作完成时递减,为0时表示有所操作已完成,出发Completed事件,调用Finish方法。需要注意的细节是设置初始值的方法需要放在异步操作的外部,异步操作的超时时间可以通过AsyncTimeoutAttribute特性的Duration属性来设置。
接下来,介绍Action的执行过程,在Controller中,包括Model绑定和验证在内的整个Action的执行是通过一个名为ActionInvoker的组件来完成的,也包含同步异步两个版本,实现类为ControllerActionInvoker和AsyncControllerActionInvoker。这个简单介绍一下Controller在选择ActionInvoker时的步骤:通过DependencyResolver以IAsyncAcionInvoker查;以IActionInvoker查;创建异步类型作为默认。不同的ControllerActionInvoker会创建其对应的ControllerDescriptor实现类,包含对应类型的ActionDescriptor。还有一点需要注意的是,Dependency默认使用会将反射创建的对象缓存到CurrentCache属性中,而不会使用当前新设置的映射重新获取。若想在程序中修改,需要手动的清空CurrentCache所对应类型中的_cache字段,部分代码如下所示:
private void ClearCachedActionInvokers()
{
var property = typeof(DependencyResolver).GetProperty("CurrentCache", BindingFlags.NonPublic|BindingFlags.Static);
var cacheActionInvoker = property.GetValue(null, null);
FieldInfo field = cacheActionInvoker.GetType().GetField("_cache", BindingFlags.NonPublic|BindingFlags.Instance);
var dictionary = field.GetValue(cacheActionInvoker) as ConcurrentDictionary<Type, object>;
dictionary.Clear();
}
在介绍筛选器的执行之前,再回顾一下相关过程,目标Action方法的最终执行是由被激活Controller的ActionInvoker决定,ActionInvoker通过调用对应的ActionDesciptor来执行被它描述的Action。筛选器使用面向切面概念(AOP)的实现,它会在在Action方法执行的前后自动执行,主要包含非业务逻辑的实现,例如授权,异常处理等。Filter作为基类包含FilterScope和Order属性,Scope包括First、Global、Controller、Action和Last,Order越小优先级越高,默认值为-1。同样Filter也有相应的Provider类,框架中原生的有FilterAttributeFilterProvider,ControllerInstanceFilterProvider和GlobalFilterCollection,简介如下表所示:
类型 | 简介 |
FilterAttribute, FilterAttributeFilterProvider | 方法GetFilter得到的每个Filter,对应的FilterAttribute特性作为其Instance属性,Scope属性取决于FilterAttribute特性是应用在Controller类型上还是Action方法上。 |
Controller, ControllerInstanceFilterProvider | Controller实现了IActionFilter,IAuthorization等四接口,本身就是一个筛选器,通过ControllerInstanceFilterProvider类型来表示针对Controller对象这种特殊筛选器的Filter。它的GetFilter方法根据ControllerContext获得对应Controller,并作为Filter的Instance属性,其Scope为First,Order为Int32.MinValue,默认最先执行。 |
GlobalFilterCollection | 全局的Filter通过GlobalFilter.Filters.Add方式来添加,默认Scope为Global。 |
在筛选器的执行顺序上,遵循先Order排序,再Scope排序,若同一筛选器特性标注在不同Scope上且AllowMultiple为false时,会选中最后的一个执行。框架使用一个FilterInfo类型统一管理内置的筛选器,之后开始按照执行顺序详细介绍各个内置的筛选器。
AuthorizationFilter,实现IAuthorizationFilter的OnAuthoration方法用于实现授权操作,成功后继续Action后续工作(Model绑定,验证,Action的执行),失败后AuthorizationContext对象的Result属性回复一个"401,Unauthorized"相应或者重定向到错误页面。它所对应的几个实现IAuthorizationFilter接口的如下表所示:
类型 | 简述与例子 |
AuthrizeAttribute | 多个Authorize特性间是"逻辑与"得关系,如下代码任何用户均无法访问。 [Authorize(Users = "Xixi", Roles="Admin")] [Authorize(Users = "XiongEr", Roles = "Admin")] public void CannotCall() { } |
RequiredHttpsAttribute | 要求请求为HTTPS形式(HttpRequest.IsSecureConnection),不满足时,如果是GET方式请求返回RedirectResult重定向请求,如果是其他方式抛出异常。 |
ValidateInputAttribute | 在Controller, Action级别上针对整个请求决定输入参数是否进行验证。例如在QueryString中放入"<script></script>" |
ValidateAntiForgeryTokenAttribute | 防止CSRF(Cross-Site Request Forgery)跨站请求伪造网络攻击,如果说XSS(Cross Site Script)是利用了用户对网站的信任,那个CSRF就是利用了站点对认证用户的信任。在之后的内容中,将继续介绍CSRF的原理和框架的预防方法。 |
ChildActionOnlyAttribute | 一般用于生成组成页面的某部分HTML,若非子Action则抛出异常。(通过DataTokens中是否包含ParentActionViewContext判断) |
接下来用蒋老师介绍的简单例子来解释CSRF的原理,假设我们奖励一个博客应用,作为博主的我们可以发表博文,而一般用户(包括匿名)可以评论。除此之外注册用户可以修改自己的绑定Email,我们将授权特性加在该Action上,看起来应该OK了,但仍然有漏洞可钻。
从上图可知,通过跳转攻击者获得用户安全令牌,通过了授权验证,说明CSRF是一种隐蔽且危害巨大的攻击,框架通过ValidateAntiForgeryTokenAttribute结合HtmlHelper的AntiForgeryToken方法有效解决了这个问题。在View中通过调用AntiForgeryToken方法,在页面中生一个值为防伪令牌字符串的hidden类型的<input>元素,并且设置一个具有HttpOnly的Cookie。防伪令牌值通过Salt,Creation,Username等内容计算得出。Cookie的名称通过应用路径base64编码值加上_RequestVerificationToken组合而成。对于加入防伪令牌的View在第一次访问或者Cookie不存在时,创建Cookie并设置HttpOnly标签,这样浏览器就无法通过脚本获得Cookie,保证了Cookie的安全。再次请求时,解密和反序列化Hidden与Cookie中相关值,比较属性即可。
ActionFilter之前介绍过的实现类包括AsyncTimeoutAttribute等,允许我们对Action执行前后添加一些额外操作,通过Result属性响应其请求。筛选器中OnActionExecuting与OnActionExecuted的执行顺序相反。正向执行时,一旦某一个ActionFilter将AcionExecuteingContext的Result设置为ActionResult对象,后续ActionFilter和目标Action将不会执行。而在逆向执行ActionFilter链时在ActionExecutedContext中设置Result不受影响,如下图所示:
ActionFilter链的异常处理过程通过对应的上下文类的Exception对象传递,ExceptionHanlded属性表明异常是否已被处理。
ExceptionFilter既可以处理ActionFilter最终抛出的异常,还可以处理ResultFilter抛出的异常。其中实现类HanldeErrorAttribute用于针对具体的异常类型来呈现对应的错误页面。同时由于ExceptionFilter链的反向执行特性,需要设置Order属性使得具体的HanldeErrorAttribute优先执行。
需要注意的一点是,HandleErrorAttribute只有在允许自定义错误时才有效, <customErrors mode="On"/>
蒋老师在书中提到,异常处理是程序员最熟悉也最难掌握的一块概念了,我确实也有这样的感受,比如说一个异常类型到底"谁来管,该怎么管,管不住怎么办",很像法制建设,需要一定的规定,但软件开发中还未有相关的通用规则。由于异常处理往往是场景驱动的,就需要一个灵活可配置的处理框架进行管理,例如微软企业库Entlib的Exception Handling Application Block(EHAB)。该库提供一种基于"策略"的异常处理方式。之后还提供了一个自动化处理异常的思路,即通过配置,自动生成try/catch过程。
简单来说就是:
异常处理策略=异常类型+异常处理器+异常后续处理方式,例子如下所示:
<configSections>
<section name="exceptionHandling" type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Configuration.ExceptionHandlingSettings, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling"/>
</configSections>
<exceptionHandling>
<exceptionPolicies>
<add name="data access policy">
<exceptionTypes>
<add type="System.Data.SqlClient.SqlException, System.Data" postHandlingAction="ThrowNewException" name="SqlException">
<exceptionHandlers>
<add name="Logging.Handler"
type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Logging.LoggingExceptionHandler"/>
<add name="Replace Handler"
type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.ReplaceHandler"
exceptionMessage="Encounter data access error."
replaceExceptionType="Xixi.DbException"/>
</exceptionHandlers>
</add>
</exceptionTypes>
</add>
</exceptionPolicies>
</exceptionHandling>
相应的调用方式为:
try { test001(); }
catch (Exception ex)
{
if (ExceptionPolicy.HandleException(ex, "data access policy"))
{
throw;
}
}
ResultFilter用于控制ActionResult的执行,属于在Action方法执行过后对ActionResult执行过程的控制,也就是对视图渲染的控制了,内容与ActionFilter相似,就不介绍了。
注:本文主要供自己学习,不妥之处望见谅。
参考资料:
[1]蒋金楠. ASP.NET MVC4框架揭秘[M]. 上海:电子工业出版社, 2012. 320-389
快速入门系列--MVC--05行为的更多相关文章
- 快速入门系列--WebAPI--01基础
ASP.NET MVC和WebAPI已经是.NET Web部分的主流,刚开始时两个公用同一个管道,之后为了更加的轻量化(WebAPI是对WCF Restful的轻量化),WebAPI使用了新的管道,因 ...
- 快速入门系列--WebAPI--03框架你值得拥有
接下来进入的是俺在ASP.NET学习中最重要的WebAPI部分,在现在流行的互联网场景下,WebAPI可以和HTML5.单页应用程序SPA等技术和理念很好的结合在一起.所谓ASP.NET WebAPI ...
- 快速入门系列--WebAPI--04在老版本MVC4下的调整
WebAPI是建立在MVC和WCF的基础上的,原来微软老是喜欢封装的很多,这次终于愿意将http编程模型的相关细节暴露给我们了.在之前的介绍中,基本上都基于.NET 4.5之后版本,其System.N ...
- 快速入门系列--MVC--01概述
虽然使用MVC已经不少年,相关技术的学习进行了多次,但是很多技术思路的理解其实都不够深入.其实就在MVC框架中有很多设计模式和设计思路的体现,例如DependencyResolver类就包含我们常见的 ...
- 快速入门系列--MVC--02路由
现在补上URL路由的学习,至于蒋老师自建的MVC小引擎和相关案例就放在论文提交后再实践咯.通过ASP.NET的路由系统,可以完成请求URL与物理文件的分离,其优点是:灵活性.可读性.SEO优化.接下来 ...
- 快速入门系列--MVC--07与HTML5移动开发的结合
现在移动互联网的盛行,跨平台并兼容不同设备的HTML5越来越盛行,很多公司都在将自己过去的非HTML5网站应用渐进式的转化为HTML5应用,使得一套代码可以兼容不同的物理终端设备和浏览器,极大的提高了 ...
- [转]快速入门系列--WebAPI--01基础
本文转自:http://www.cnblogs.com/wanliwang01/p/aspnet_webapi_base01.html ASP.NET MVC和WebAPI已经是.NET Web部分的 ...
- WPF快速入门系列(4)——深入解析WPF绑定
一.引言 WPF绑定使得原本需要多行代码实现的功能,现在只需要简单的XAML代码就可以完成之前多行后台代码实现的功能.WPF绑定可以理解为一种关系,该关系告诉WPF从一个源对象提取一些信息,并将这些信 ...
- WPF快速入门系列(1)——WPF布局概览
一.引言 关于WPF早在一年前就已经看过<深入浅出WPF>这本书,当时看完之后由于没有做笔记,以至于我现在又重新捡起来并记录下学习的过程,本系列将是一个WPF快速入门系列,主要介绍WPF中 ...
- Qt快速入门系列教程目录
Qt快速入门系列教程目录
随机推荐
- 转 :meta name的含义:<META http-equiv=Content-Type content="text/html; charset=gb2312">
meta是什么?meta其实是html语言head区的一个辅助性标签.在几乎所有的网页里,我们都可以看到类似下面这段html代码:<META http-equiv=Content-Type co ...
- java解析json
1:下载另外一个Java的小包就可以了: http://www.JSON.org/java/json_simple.zip 里面有源码和文档例题和编程的lib包:编程只需要json_simple.ja ...
- Codeforces Zip-line 650D 345Div1D(LIS)
传送门 大意:给出一个序列,求修改一个数过后的最长上升子序列. 思路:可以用主席树在线搞,也可以用树状数组离线搞,明显后者好写得多.我们首先读取所有的询问,然后就把询问绑在给出的位置,然后我们正向做一 ...
- PYTHON学习之路_PYTHON基础(1)
学习内容: 1.Python介绍 2.Python程序初接触和变量 3.Python用户交互 4.Python数据类型 5.Python循环if...(elif)...else 6.Python循环w ...
- ProceedingJoinPoint获取当前方法
aspectJ切面通过ProceedingJoinPoint想要获取当前执行的方法: 错误方法: Signature s = pjp.getSignature(); MethodSignatu ...
- Lua与C++互相调用(上)
int main1(int argc, const char * argv[]) { lua_State* L = luaL_newstate();//创建栈 luaopen_base(L); lua ...
- Oracle第三方ado.net数据提供程序
原文地址:http://www.infoq.com/cn/news/2009/06/oracleclient_deprecated 这项决定有部分原因是基于目前Oracle的第三方ADO.NET数据提 ...
- Spring 使用JSTL标签显示后台数据
1. 先上项目结构图,其中config包下的代码文件参见前一篇博客 http://www.cnblogs.com/njust-ycc/p/6123505.html 引包: 2. 主要代码 (1)U ...
- JDBC数据类型
JDBC数据类型 JDBC驱动程序Java数据类型转换到适当的JDBC类型然后再将它发送到数据库.它使用默认的大多数数据类型映射.例如,一个Java int转换成一个SQL INTEGER.创建默认映 ...
- 浏览器兼容性小记-DOM篇(二)
1.DOM中的所有节点都继承自Node类型,IE9之前将DOM节点作为COM对象来实现:每个DOM节点都有一个nodeType属性来表明节点类型,总共有12个类型: Node.ELEMENT_NODE ...