包含在ASP.NET MVC中的过滤器

在深入研究如何编写过滤器之前,首先看看包含在ASP.NET MVC中的过滤器。

ASP.NET MVC包括了如下3种即开即用的动作过滤器:

Authorize:该过滤器用于限制对控制器或控制器动作的访问。

HandleError:该过滤器用来指定一个处理异常的动作,这个异常是从动作方法的内部抛出的。

OutputCache:该过滤器用来为动作方法提供输出的缓存。

接下来将依次深入讨论这3个过滤器。

1  Authorize

AuthorizeAttribute是包含在ASP.NET MVC中默认的授权过滤器。可以使用它来限制对动作方法的访问。将该特性运用到控制器上可以迅速将其运用到每个动作方法中。

在运用该过滤器时需要牢记如下内容:

在运用该特性时,可以指定一个逗号来划分角色(Role)或用户(User)的列表。如果指定了一个角色的列表,那么为了动作方法的执行,用户必须是其中一个角色中的成员。同样的,如果指定了一个用户的列表,那么当前用户的名称必须在该列表中。

为什么不使用已有的、构建在ASP.NET中的URL授权

保护使用了Web Forms的应用程序的常见方式是使用URL授权。例如,如果具有一个管理部分,而且希望将其限制为那些位于管理员(Admins)角色中的用户,那么可能会把所有管理页面都放在管理文件夹中并拒绝除位于管理员角色中以外的任何人访问子文件夹。

对于MVC而言,这种方法不会很好地运转,原因有两个:

请求不再映射到物理目录中。

进入到同一个控制器的路由可能不止一个。

对于MVC而言,理论上讲,是可以使用AdminController封装应用程序的管理功能,然后在web.config根文件中设置URL授权,进而阻止访问任何以/Admin开头的请求。然而,这并不是万无一失的,也有可能存在另外一个路由,它可能在不经意间映射到AdminController中。

例如,后面将会讨论的,如果决定切换默认路由中的{controller}和{action}的顺序,那么此时,/Index/Admin是默认的管理页面的URL,而且URL授权也不再阻止它。

安全性较好的一个方法是总是尽可能紧紧地将安全检查放到需要保护的对象上。虽然可能会在更高层次的堆栈上具有其他检查,但是最终希望保护的是实际的资源。在这种情况下,不希望依靠路由选择和URL授权来保护控制器;真正想要保护的是控制器自身。AuthorizeAttribute正好用于此用途。

如果没有指定任何角色或用户,那么为了调用动作方法,必须只验证当前的用户。这是阻止非验证用户访问特殊控制器动作的一个简单的方法。

如果用户试图访问运用了该特性的动作方法且在授权检查中失败,那么过滤器将引发服务器返回一个401 Unauthorized的HTTP状态代码。

对于启用了表单验证且在web.config中指定了注册URL的情形,ASP.NET将处理该响应代码并将用户重新引向注册页面。这是ASP.NET已有的行为,对于ASP.NET MVC也不是新鲜事物。

产品小组的话:

一开始,我们将PrincipalPermissionAttribute看作是保护控制器动作的一个可行的解决方案,但是碰到了很多问题。首先,在将其运用到类时,PrincipalPermissionAttribute将在安全检查失败而试图实例化Controller类时导致一个异常。因为我们希望当安全检查失败时可能会有其他过滤器运行(例如,日志记录过滤器),所以这并不是我们想要的行为。

其次,我们希望能控制产生的状态代码。而PrincipalPermissionAttribute只是抛出一个SecurityException。

下面看一个使用的简单示例。在下面的代码中,管理控制器只限制于Admins和SuperAdmins角色的成员。注意,角色是通过逗号隔开的。过滤器将忽略逗号之间的空白以允许提高在运用该特性时的可读性。


  1. [Authorize(Roles="Admins, SuperAdmins")]  
  2. public class AdminController  
  3. {  
  4.     //Only admins should see this.  
  5.     public ActionResult Index()   
  6.     {  
  7.       return View();  
  8.     }  
  9.      
  10.     //Only admins should see this.  
  11.     public ActionResult DeleteAllUsers()  
  12.     {  
  13.         //Thankfully, this is secured by the Authorize attribute.  
  14.     }  

在认真思考上述示例之后,本书作者认识到我们并不希望任何管理员或系统管理员(superadmin)能够调用DeleteAllUsers的动作。事实上,我们只信任由Phil完成该动作,所以这里运用了一个更为具体的授权,即只允许用户Phil来调用该动作。当运用了多个授权过滤器时,用户为了调用动作必须满足所有的授权过滤器。因此,在这种情况下,Phil必须同时是管理员(Admins)角色的成员也是系统管理员(SuperAdmins)角色的成员。

注意:

另一件需要注意的事情是,一般来说:甚至在类似情况下,使用角色而不是具体的用户名将更有意义。更好的方法可能是创建一个名为CanDeleteAllUsers的角色并将Phil添加到该角色中,然后运用一个指定了该角色的授权过滤器。


  1. [Authorize(Roles="Admins, SuperAdmins")]  
  2. public class AdminController  
  3. {  
  4.     //Only admins should see this.  
  5.     public ActionResult Index()   
  6.     {  
  7.       return View();  
  8.     }  
  9.      
  10.     //Only Phil should do this.  
  11.     [Authorize(Users="Phil")]  
  12.     public ActionResult DeleteAllUsers()  
  13.     {  
  14.         //…  
  15.     }  

在最后的示例中,切换到一个为个人用户提供用户管理的控制器中;不需要指定用户和角色。在这种情况下,控制器可能向所有通过验证的人打开大门。


  1. [Authorize]  
  2. public class UsersController  
  3. {  
  4.     public ActionResult ManageProfile()  
  5.     {  
  6.         //…  
  7.         return View();  
  8.     }  
  9. }

2.OutputCache

OutputCacheAttribute用来缓存动作方法的输出。该特性是到ASP.NET输出缓存功能内的连接,而且提供了在使用@OutputCache页面指令时得到的大部分相同的API和行为。本章后面将讨论两者之间微小的区别。

因为输出缓存是ASP.NET非常著名的功能,所以本节没有深入讨论缓存行为自身的细节,而是将注意力集中于MVC实现上。此时,可能会有一个问题,"为什么不在视图中只使用@OutputCache指令呢?"

对于MVC而言,首先在选中视图之前,执行控制器动作。因此,将输出缓存放到视图上,这实际上将缓存视图的输出,而动作方法自身将仍然在每个请求上执行,这样就否定了缓存输出的大部分优点。

通过将该特性运用到动作方法中,过滤器随后可以确定缓存是否有效并跳过动作方法的调用,直接呈现缓存的内容。

API

表8-1列出了OutputCacheAttribute类的属性。这些设置用来控制OutputCache过滤器如何执行其缓存动作。

表8-1  OutputCacheAttribute类的属性


属    性


说    明


CacheProfile


即将使用的缓存设置的名称。它允许将缓

存的配置放到web.config文件中而不是

特性中。随后,该特性可以通过该属性

来引用配置设置


Duration


指定了将输出存储到缓存中的秒数


Location


指定了可能缓存内容的地方。枚举

OutputCacheLocation包含了允许的位置:

Any、Client、Downstream、Server、

None、ServerAndClient等


NoStore


将HTTP题头“Cache-Control: Private,

no-store”设置为阻止浏览器缓存响应。

它等价于调用Response.Cache.SetNoStore

(续表)  


属    性


说    明


SqlDependency


特殊格式化的字符串值,包含了一组

输出缓存所依赖的数据库和表的名称

对。当这些表中的数据发生了更改,

则缓存失效


VaryByContentEncoding


在ASP.NET 3.5中引入,这是以逗号

分隔的内容编码的列表,用于变更缓存


VaryByCustom


确定是否基于对Global.asax.cs文件中

的GetVaryByCustomString的调用缓存

新版本的输出。这为开发人员提供了

在缓存时完全的控制


VaryByHeader


基于http题头改变缓存。例如,可能使

用它基于Accept-Language题头缓存

不同版本的输出


VaryByParam


用于指定哪一个QueryString参数导

致了一个新版本的输出被缓存

与@OutputCache指令之间的区别

由于应用程序开发的ASP.NET MVC模型与Web Forms模型之间的区别,所以还有一个选项不会翻译成输出缓存的过滤器,即VaryByControl属性。因此,该属性在OutputCacheAttribute中是没有的。

使用示例

下面将讨论输出缓存过滤器常见的使用模式。在很多情况下,可能希望仅缓存动作的输出一段很短的时间。在下面的代码示例中,缓存了默认的About动作的输出,持续时间为60秒。修改方法的实现以显示时间:


  1. [OutputCache(Duration=60, VaryByParam="none")]  
  2. public ActionResult About()   
  3. {  
  4.     ViewData["Title"] = "This was cached at " + DateTime.Now;  
  5.     return View();  

在考虑前面的示例时,我们希望不必每次修改持续时间时就重新编译代码。相反,可能希望在配置文件中设置持续时间。

幸运的是,在web.config中已经存在一个输出缓存的设置部分。下面的样本阐述了如何将缓存设置添加到web.config中:


  1. <system.web> 
  2.   <caching> 
  3.     <outputCacheSettings> 
  4.       <outputCacheProfiles> 
  5.         <add name="MyProfile" duration="60" varyByParam="none" /> 
  6.       </outputCacheProfiles> 
  7.     </outputCacheSettings> 
  8.   </caching> 
  9. </system.web> 

注意,添加了一个名为MyProfile的缓存配置文件,现在可以修改输出缓存过滤器来通过CacheProfile属性读取该配置文件的设置:


  1. [OutputCache(CacheProfile="MyProfile")]  
  2. public ActionResult About()   
  3. {  
  4.     ViewData["Title"] = "This was cached at " + DateTime.Now;  
  5.     return View();  
  6. }

3  异常过滤器

HandleErrorAttribute是包含在ASP.NET MVC中的默认异常过滤器。如果动作方法抛出一个未处理的异常,且该异常与指定的异常类型匹配或源自它,那么就可以使用该过滤器指定需要处理的异常类型以及需要显示的视图(如果需要的话,还包括主视图)。

默认情况下,如果没有指定异常类型,那么过滤器将处理所有的异常。如果没有指定视图,那么过滤器将默认处理名为Error的视图。默认的ASP.NET MVC项目在Shared文件夹中包含了一个名为Error.aspx的视图。

请看下面的示例:


  1. [HandleError(ExceptionType = typeof
    (ArgumentException), View="ArgError")]  
  2. public ActionResult GetProduct(string name)  
  3. {  
  4.   if(name == null)  
  5.   {  
  6.     throw new ArgumentNullException("name");  
  7.   }  
  8.   return View();  

因为ArgumentNullException继承自ArgumentException,所以将null传递到该动作方法中将导致显示ArgError视图。

在一些情况下,可能希望将多个异常过滤器运用到同一个动作方法中。那么,为了能将最具体的异常类型放在最前面而将较不明确的放在后面,就需要指定这些情况的顺序,这是很重要的。例如,在下面的代码片断中:


  1. //This is WRONG!  
  2. [HandleError(Order=1, ExceptionType=typeof(Exception))  
  3. [HandleError(Order=2, ExceptionType=typeof(ArgumentException),   
  4. View="ArgError")]  
  5. public ActionResult GetProduct(string name)  
  6. {  
  7.   …  

第一个过滤器要比其后的过滤器更为通用,它将处理所有的异常,而始终不会给第二个过滤器任何机会来处理异常。为了修复此问题,只需将过滤器从最具体到最不具体进行排序。


  1. //This is BETTER!  
  2. [HandleError(Order=1, ExceptionType=typeof(ArgumentException),   
  3. View="ArgError")  
  4. [HandleError(Order=2, ExceptionType=typeof(Exception)]  
  5. public ActionResult GetProduct(string name)  
  6. {  
  7.   …  

当该异常过滤器处理一个异常时,它创建了一个HandleErrorInfo类的实例并设置在呈现Error视图时ViewDataDictionary实例的Model属性。

表8-2展示了HandleErrorInfo类的属性:

表8-2  HandleErrorInfo类的属性


属    性


说    明


Action


抛出异常的动作的名称


Controller


在其中抛出异常的控制器的名称


Exception


抛出的异常

注意,该过滤器没有捕捉到在没有启用自定义错误时在调试构建中的异常。过滤器只是检查HttpContext.IsCustomErrorEnabled以确定是否处理该异常。这样做的理由是允许通过信息更丰富的Yellow Screen of Death来显示开发阶段与异常有关的信息。

转自:http://book.51cto.com/art/201006/205387.htm

时间: 2024-09-15 19:50:00

包含在ASP.NET MVC中的过滤器的相关文章

asp.net MVC 中的过滤器 ,一个路径设置对应一个过滤器类的对象吗

问题描述 asp.net MVC 中的过滤器 ,一个路径设置对应一个过滤器类的对象吗 asp.net MVC 中的过滤器 ,一个路径设置对应一个过滤器类的对象吗 .还是多个路由对应一个过滤器对象.有没有大神给讲解下路由和过滤器的对应关系代码如下:控制器代码: [CustAuthorize(""list"")] [Route(""list"")] public ActionResult List() { return View(

ASP.NET MVC中你必须知道的13个扩展点

ScottGu在其最新的博文中推荐了Simone Chiaretta的文章13 ASP.NET MVC extensibility points you have to know,该文章为我们简单介绍了ASP.NET MVC中的13个扩展点.Keyvan Nayyeri(与Simone合著了Beginning ASP.NET MVC 1.0一书)又陆续发表了一些文章,对这13个扩展点分别进行深入的讨论.我将在 以后的随笔中对这些文章逐一进行翻译,希望能对大家有所帮助. ASP.NET MVC设计

ASP.NET MVC学习之过滤器篇(2)

原文:ASP.NET MVC学习之过滤器篇(2) 下面我们继续之前的ASP.NET MVC学习之过滤器篇(1)进行学习.   3.动作过滤器 顾名思义,这个过滤器就是在动作方法调用前与调用后响应的.我们可以在调用前更改实际调用的动作,也可以在动作调用完成之后更改最终返回的结果,当然很多人一定不太明白这个到底可以干什么, 下面我们举一个比较实际的例子:   相信理解过网站的安全的一定知道跨站请求(CSRF具体可以自行百度,这里我就不去解释了),当然也有解决方案,那就是给页面中增加一个识别码,当页面

ASP.NET MVC学习之过滤器篇(1)

原文:ASP.NET MVC学习之过滤器篇(1) 一.前言 继前面四篇ASP.NET MVC的随笔,我们继续向下学习.上一节我们学习了关于控制器的使用,本节我们将要学习如何使用过滤器控制用户访问页面.   二.正文 以下的示例建立在ASP.NET MVC 4之上(VS2012)   1.授权过滤器 只要涉及用户的网站,都一定会涉及到什么权限的用户可以访问哪个页面.对于新手而言可能都在每个页面中单独写这个功能方法,导致的后果就是大量重复的代码,并且不便于以后的变动.有用一定经验之后,就会采用集中控

ASP.NET MVC中加载WebForms用户控件(.ascx)

原文:ASP.NET MVC中加载WebForms用户控件(.ascx) 问题背景 博客园博客中的日历用的是ASP.NET WebForms的日历控件(System.Web.UI.WebControls.Calendar),它会为"上一月"."下一月"的链接生成"__doPostBack()"的js调用,如下图: 目前发现它会带来两个问题: 1. 不支持IE10: 2. 某些电脑不允许执行__doPostBack. 问题提炼 前提: 我们想以最低

ASP.NET MVC中使用DropDownList地详解

在ASP.NET MVC中,尽管我们可以直接在页面中编写HTML控件,并绑定控件的属性,但更方便的办法还是使用HtmlHelper中的辅助方法.在View中,包含一个类型为HtmlHelper的属性Html,它为我们呈现控件提供了捷径.   我们今天主要来讨论Html.DropDownList的用法,首先从Html.TextBox开始. Html.TextBox有一个重载方法形式如下: public static string TextBox(this HtmlHelper htmlHelper

在ASP.NET MVC中如何使用异步控制器

在ASP.NET MVC中使用异步操作的时候,我有这么几个关注点.异步操作何时提高我应用的性能,什么时候没改善?     在ASP.NET MVC中到处使用异步操作真的好吗?    对于可等待的(awaitable)方法: 当查询数据库时(通过EF/BHibernate/其他的ORM)应该使用async/await关键字吗?    在一个单独的操作方法中,异步地查询数据库可以使用await关键字多少次? 当一个action必须执行多个独立的长期运行的操作时,异步action方法是很有用的. 假设

Asp.net MVC中获取控制器的名称的方法_实用技巧

1.视图中 string controller = ViewContext.RouteData.Route.GetRouteData(this.Context).Values["controller"].ToString(); string controller = ViewContext.RouteData.Values["controller"].ToString(); 2.控制器的action中 string controller = RouteData.Ro

如何在 ASP.NET MVC 中集成 AngularJS(3)

今天来为大家介绍如何在 ASP.NET MVC 中集成 AngularJS 的最后一部分内容. 调试路由表 - HTML 缓存清除 就在我以为示例应用程序完成之后,我意识到,我必须提供两个版本的路由表:一个运行在调试模式的应用程序下和一个运行在发布模式的应用程序下.在调试模式下,JavaScript 文件在未使用压缩功能的情况下会被下载.如果想要调试并在 JavaScript 控制器中设置断点,这是必须的.事实上,路由表的产生版本也出现了一些挑战,由于产生路由代码使用的是 JavaScript