• asp.net MVC之 自定义过滤器(Filter)


    一、系统过滤器使用说明

    1、OutputCache过滤器

    OutputCache过滤器用于缓存你查询结果,这样可以提高用户体验,也可以减少查询次数。它有以下属性:

    Duration :缓存的时间, 以秒为单位 ,理论上缓存时间可以很长,但实际上当系统资源紧张时,缓存空间还是会被系统收回。

    VaryByParam :以哪个字段为标识来缓存数据,比如当“ID”字段变化时,需要改变缓存(仍可保留原来的缓存),那么应该设VaryByParam为"ID"。这里你可以设置以下几个值:

    • * = 任何参数变化时,都改变缓存。
    • none = 不改变缓存。

    以分号“;”为间隔的字段名列表 = 列表中的字段发生变化,则改变缓存。

    Location :缓存数据放在何处。缓存位置很重要,如果存在服务器上,那么所有用户看到的缓存视图都会一样,如果存在客户端,那么用户只会看到自己的缓存。比如:如果是一些私人信息,那就不能存在服务器上。你可以设置以下值:

    • · Any :默认值,输出缓存可位于产生请求的浏览器客户端、参与请求的代理服务器(或任何其他服务器)或处理请求的服务器上。
    • · Client:输出缓存位于产生请求的浏览器客户端上。
    • · Downstream 输出缓存可存储在任何 HTTP 1.1 可缓存设备中,源服务器除外。这包括代理服务器和发出请求的客户端。 
    • · Server:输出缓存位于处理请求的 Web 服务器上。
    • · None:对于请求的页,禁用输出缓存。
    • · ServerAndClient:输出缓存只能存储在源服务器或发出请求的客户端中。代理服务器不能缓存响应。
    •   NoStore :该属性定义一个布尔值,用于决定是否阻止敏感信息的二级存储。

    以下给出一个简单的例子,在页面上显示一个时间,设置缓存为10秒,在10秒刷新,输出的值都不会改变。

    [OutputCache(Duration=5)]
        public ActionResult Index(string name)
        {
            return Content(DateTime.Now.ToString());
        }

    除了直接在Action或者类的定义前加上属性,也可以使用配置文件,这样就可以动态配置你的缓存模式了。

    在<system.web>节中,添加如下配置:

    <outputCacheSettings>
        <outputCacheProfiles>
            <add name="Cache1Hour" duration="3600" varyByParam="none"/>
        </outputCacheProfiles>
    </outputCacheSettings>
    </caching>

    那么在Controller中可以这样使用:

    [OutputCache(CacheProfile="Cache1Hour")]
    public string Index()
    {
       return DateTime.Now.ToString("T");
    }

    [扩展]在已经缓存的页面上添加动态内容

    为了提高用户体验,我们会使用缓存技术,但是有时我们会需要在页面上改变内容,如:提供一些动态信息、广告的变化等。

    此时我们可以调用 HttpResponse.WriteSubstitution() 方法。

    @Response.WriteSubstitution(News.RenderNews);

    其中News.RenderNews是一个静态方法,它的定义如下,这个方法用来随机显示三条广告词。

    public class News
    {
    	public static string RenderNews(HttpContext context)
    	{
    		var news = new List<string> 
    		{ 
    		   "Gas prices go up!", 
    			"Life discovered on Mars!", 
    			 "Moon disappears!" 
    		};
    			
    		var rnd = new Random();
    		return news[rnd.Next(news.Count)];
    	}
    }

    将 Response.WriteSubstitution()写成扩展方法的示例:

    public static class AdHelper
    	{
    		public static void RenderBanner(this HtmlHelper helper)
    		{
    			var context = helper.ViewContext.HttpContext;
    			context.Response.WriteSubstitution(RenderBannerInternal);
    		}
    
    	 private static string RenderBannerInternal(HttpContext context)
    		{
    			var ads = new List<string> 
    				{ 
    					"/ads/banner1.gif", 
    					"/ads/banner2.gif", 
    					"/ads/banner3.gif" 
    				};
    
    			var rnd = new Random();
    			var ad = ads[rnd.Next(ads.Count)];
    			return String.Format("<img src='{0}' />", ad);
    		}
    
    	}

    调用方法如下:

    <% Html.RenderBanner(); %>

    2、ActionName

    ActionName用于规定Action的名称,当使用此过滤器后,MVC将不再理会路径中的Action名称,而是用标记中的ActionName代替方法名中的ActionName。例如:

    [ActionName("关于")]
    public ActionResult About()
    {
        return View();
    }

    此时,当访问/Home/About时匹配不到Action,需要访问/Home/关于才能访问该Action,并且使用的是名为"关于"的视图。

    3、NonAction

    NonAction标记一个Action只是一个普通的方法,不作为MVC的Action。如:

    public class HomeController : Controller
    	{
    		[NonAction]
    		public ActionResult Index(string name)
    		{
    			return Content(DateTime.Now.ToString());
    		}
    	}

    此时访问/Home/Index将找不到Action。

    4、RequireHttps

    强制使用Https重新发送请求;如:

    public class HomeController : Controller
    	{
    		[RequireHttps]
    		public ActionResult Index(string name)
    		{
    			return Content(DateTime.Now.ToString());
    		}
    	}

    如果请求:http://localhost/Home/Index 将跳转到 https://localhost/Home/Index。

    5、ValidateInput

    该Action是否过滤Html等危险代码(ASP.NET MVC在aspx中设置<%@ Page 的属性无法完成等同任务。)

    如以下代码:

    public class HomeController : Controller
    	{
    		[ValidateInput(true)]
    		public ActionResult Index(string name)
    		{
    			return Content(DateTime.Now.ToString());
    		}
    	}

    上述代码表示开启安全验证,当输入以下路径时:

      http://localhost:3833/home/index?name=%3Ca%3E123%3C/a%3E    //http://localhost:3833/home/index?name=<a>123</a>

    程序报如下错误:

    “/”应用程序中的服务器错误。


    从客户端(name="<a>123</a>")中检测到有潜在危险的 Request.QueryString 值。

    6、AllowHtml

    AllowHtml用于 禁用某个 字段、属性 的验证 ,则可以使用MVC3中的AllowHtml属性实现。如:

    namespace Mvc权限控制.Controllers
    {
    	public class HomeController : Controller
    	{
    		public ActionResult Index(Content c)
    		{
    			return View(); ;
    		}
    	}
    
    	public class Content
    	{
    		public int Id { get; set; }
    
    		[AllowHtml]
    		public string Body { get; set; }
    	}
    }

    页面代码:

    <body>
    	<div>
    		<form action="/Home/Index" method="post">
    			请输入Id:<input type="text" name="Id" />
    			请输入姓名:<input type="text" name="Body" />
    			<input type="submit" value="提交" />
    		</form>
    	</div>
    </body>

    注意,如果将上面Body的属性AllowHtml标记去掉,将报如下错误:

    “/”应用程序中的服务器错误。


    从客户端(Body="<a>123</a>")中检测到有潜在危险的 Request.Form 值。

    7、SessionState自定义Session控制

    SessionState只能应用于Controller,不能作用于某一个Action。可选值如下:

    1. Default = 0,使用默认 ASP.NET 逻辑来确定请求的会话状态行为。默认逻辑是寻找 System.Web.IHttpHandler 中是否存在标记会话状态接口。
    2. Required = 1,为请求启用完全的读写会话状态行为。此设置将覆盖已通过检查请求的处理程序确定的任何会话行为。
    3. ReadOnly = 2,为请求启用只读会话状态。这意味着会话状态无法更新。此设置将覆盖已通过检查请求的处理程序确定的任何会话状态行为。
    4. Disabled = 3,未启用会话状态来处理请求。此设置将覆盖已通过检查请求的处理程序确定的任何会话行为。
    [SessionState(System.Web.SessionState.SessionStateBehavior.ReadOnly)]

    8、Http动作过滤器

    [HttpGet] [HttpSet] [HttpDelete] [HttpPut] 此4个过滤器用于筛选Http请求,[HttpGet]只处理Get请求,[HttpPost]只处理Post请求,[HttpDelete]只处理 Delete请求,[HttpPut]只处理put请求。

    9、ValidateAntiForgeryToken

    防止跨站请求攻击时会在cookie添加一个随机项,然后添加一个随机数到表单里的<input type="hidden" />而ValidateAntiForgeryToken就是用于检测两项是否相等。主要包括: (1)请求的是否包含一个约定的AntiForgery名的cookie

    (2)请求是否有一个Request.Form["约定的AntiForgery名"],约定的AntiForgery名的cookie和Request.Form值是否匹配。

    10.AsyncTimeout 

    异步访问过期设置

    11.HandleError 错误过滤器

    HandleErrorAttribute中,提供了4种基本属性:

    1. ExceptionType:指定处理的异常类型
    2. View:指定显示的View
    3. Master:指定要套用的Master页面
    4. Order:设置执行的顺序

    12.AllowAnonymous 

    身份验证过滤器,允许匿名用户访问

    13.ChildActionOnly 

    声明该Action不能直接通过Url 访问但可以作为某一个Action的子Action访问。

    二、过滤器的类别以及执行顺序:

    在Asp.net MVC中一共有4种过滤器,并且按照如下顺序依次执行。

    1. 授权筛选器:AuthorizationFilters
    2. 动作筛选器:ActionFilters
    3. 响应筛选器:ResultFilters
    4. 异常筛选器:ExceptionFilters

    Controller最终是通过Controller的ExecuteCore完成的,这个方法通过调用ControllerActionInvoker的InvodeAction方法完成最终对于Action的调用。

    其时序图如下:

    三、自定义过滤器接口

    ActionFilterAttribute的定义如下,允许我们在Action执行之前或者之后,在Action的返回结果被处理之前或者之后进行自定义处理。

    using System;
    namespace System.Web.Mvc
    {
      [AttrubiteUsage(ArrtibuteTargets.Class | AttributeTargets.Method,Inherited = true,AllowMultiple = false)]
      public abstract class ActionFilterAttribute : FilterAttribute,IActionFilter,IResultFilter
      {     public virtual void OnActionExecuting(ActionExecutingContext filterContext){}     public virtual void OnActionExecuted(ActionExecutedContext filterContext){}     public virtual void OnResultExecuting(ResultExecutingContext filterContext){}     public virtual void OnResultExecuted(ResultExecutedContext filterContext){}   } }

    我们需要实现IActionFilter接口:

    public interface IActionFilter
    {
      void OnActionExecuting(ActionExecutingContext filterContext);
      void OnActionExecuted(ActionExecutedContext filterContext);
    }

    对于ResultFilter来说,我们需要实现接口IResultFilter

    public interface IResultFilter
    {
      void OnResultExecuting(ResultExecutingContext filterContext);
      void OnResultExecuted(ResultExecutedContext filterContext);
    }

    AuthorizationFilter和ExceptionFilter都比较简单,只有一个方法。

    public interface IAuthorizationFilter
    {
      void OnAuthorization(AuthorizationContext filterContext);
    }
    public interface IExceptionFilter
    {
      void OnException(ExceptionContext filterContext);
    }

    1、自定义Filter

    自定义Filter需要继承ActionFilterAttribute抽象类,重写其中需要的方法,来看下ActionFilterAttribute类的方法签名。

    //表示所有操作-筛选器特性的基类。
    	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
    	public abstract class ActionFilterAttribute : FilterAttribute, IActionFilter, IResultFilter
    	{
    		protected ActionFilterAttribute();
    		//	在Action执行之后由 MVC 框架调用。
    		public virtual void OnActionExecuted(ActionExecutedContext filterContext);
    		//	 在Action执行之前由 MVC 框架调用。
    		public virtual void OnActionExecuting(ActionExecutingContext filterContext);
    		//	 在执行Result后由 MVC 框架调用。
    		public virtual void OnResultExecuted(ResultExecutedContext filterContext);
    		//	 在执行Result之前由 MVC 框架调用。
    		public virtual void OnResultExecuting(ResultExecutingContext filterContext);
    	}

    因此自定义过滤器可以选择适当的方法来重写方可。下面来举个简单的例子:检查登录状态的过滤器,没有登录则跳转到登录页

    控制器代码:

    [CheckLogin]  //此处为自定义属性,要引用相应的命名空间
    public ActionResult Index()
    {
    	return View();
    }
    
    public ActionResult Login()   //此Action自动往cookie里写入登录信息
    {
    	HttpCookie hcUserName = new HttpCookie("username","admin");
    	HttpCookie hcPassWord = new HttpCookie("password","123456");
    	System.Web.HttpContext.Current.Response.SetCookie(hcUserName);
    	System.Web.HttpContext.Current.Response.SetCookie(hcPassWord);
    	return View();
    }

    过滤器代码:

    public class CheckLogin : ActionFilterAttribute
    	{
    		//在Action执行之前 乱了点,其实只是判断Cookie用户名密码正不正确而已而已。
    		public override void OnActionExecuting(ActionExecutingContext filterContext)
    		{
    			HttpCookieCollection CookieCollect = System.Web.HttpContext.Current.Request.Cookies;if (CookieCollect["username"] == null || CookieCollect["password"] == null)
    			{
    				filterContext.Result = new RedirectResult("/Home/Login");
    			}
    			else
    			{
    				if (CookieCollect["username"].Value != "admin" && CookieCollect["password"].Value != "123456")
    				{
    					filterContext.Result = new RedirectResult("/Home/Login");
    				}
    			}
    		}
    	}//本示例贪图方便,将要跳转到的Action放在同一个Controller下了,如果将过滤器放到Controller类顶部,则永远也跳不到这个LoginAction。

    此过滤器实现的效果是,当用户Cookie中用户名和密码不正确则跳转到登录页,注意 过 滤器也可以放在整个Controller类的顶部,表示该Controller下的所有Action都执行该项检查。 这样一来,控制器里的代码非常漂亮,再也不用所有的Action里都充斥着判断登录的代码了。

    2、带参数的自定义Filte r

    首先,还是按照之前添加自定义过滤器的方法,添加一个自定义过滤器,只是里面多了一个属性,代码如下:

    public class FilterAttribute : ActionFilterAttribute
    	{
    		public string Message { get; set; }
    
    		public override void OnActionExecuting(ActionExecutingContext filterContext)
    		{
    			base.OnActionExecuting(filterContext);
    			filterContext.HttpContext.Response.Write("Action执行之前" + Message + "<br />");
    		}
    
    		public override void OnActionExecuted(ActionExecutedContext filterContext)
    		{
    			base.OnActionExecuted(filterContext);
    			filterContext.HttpContext.Response.Write("Action执行之后" + Message + "<br />");
    		}
    
    		public override void OnResultExecuting(ResultExecutingContext filterContext)
    		{
    			base.OnResultExecuting(filterContext);
    			filterContext.HttpContext.Response.Write("返回Result之前" + Message + "<br />");
    		}
    
    		public override void OnResultExecuted(ResultExecutedContext filterContext)
    		{
    			base.OnResultExecuted(filterContext);
    			filterContext.HttpContext.Response.Write("返回Result之后" + Message + "<br />");
    		}
    	}

    然后在调用过滤器的时候,添加上该参数,Controller代码如下:

    [Filter(Message="刘备")]  //参数给上
            public ActionResult Index()
            {
                return View();
            }

    输出结果如下:

    如果标签打到Controller上的话,TestFilterAttributeFilter将作用到Controller下的所有的Action。

    默认情况下Action上打了某个自定义标签后,虽然在Controller上也打上了此标签,但它只有Action上的标签起作用了。

    补充:如果Action没有打上该标签,那么Controller上的标签便会被执行。

    如果想让Action上的标签执行一次,然后Controller上的标签也执行一次,那么应该如何操作呢?

    我们只需在FilterAttribute类的定义上打上标记[AttributeUsage(AttributeTargets.All, AllowMultiple = true)]即可【下面类的最上面红色字体部分】,也就是让其成为可以多次执行的Action 代码如下:

        [AttributeUsage(AttributeTargets.All,AllowMultiple = true)]
        public class FilterAttribute : ActionFilterAttribute
        {
            public string Message { get; set; }
            ......
      }

    3、全局过滤器

    有时 我们想有些公共的方法需要 每个Action都执行,但是又不想再每一个Controller上都打上Action标签,怎么办?幸好Asp。Net MVC3带来了一个美好的东 西,全局Filter。而怎么注册全局Filter呢?答案就在Global.asax中。让我们看以下代码,我是如何将上面我们定义的 TestFilterAttribute 注册到全局Filter中。

        public static void RegisterGlobalFilters(GlobalFilterCollection filters)
            {
                filters.Add(new HandleErrorAttribute());
                //注册全局过滤器
                filters.Add(new TestFilterAttribute() { Message="全局"});
            }

    这样就每个Action都会执行此过滤器,而不必每个Controller顶部都加上标签。

  • 相关阅读:
    通过哪吒动漫豆瓣影评,带你分析python爬虫与BeautifulSoup快速入门
    带着canvas去流浪系列之九 粒子动画
    带着canvas去流浪系列之八 碰撞
    Python小数据保存,有多少中分类?不妨看看他们的类比与推荐方案...
    免费试用 | 多模 NoSQL 服务GeminiDB for Cassandra 全球首发
    Vue+ElementUI项目使用webpack输出MPA
    nmon
    补习系列(12)-springboot 与邮件发送
    从React 编程到"好莱坞"
    百度网盘API的操作--PCS 百度个人云存储 上传 ,下载文件
  • 原文地址:https://www.cnblogs.com/zxtceq/p/6076950.html
Copyright © 2020-2023  润新知