在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
这里分享MVC里的Filters(过滤器),什么是MVC里的过滤器,他的作用是什么? 过滤器的请求处理管道中注入额外的逻辑。他们提供了一个简单而优雅的方式来实现横切关注点。这个术语是指所有对应用程序的功能,用于不适合整齐地进入任何一个地方,所以这将打破关注分离的模式。经典的横切关注点的记录,授权和缓存。 所谓的过滤器,因为这个词被用于在其他Web应用程序相同的设施框架,包括Ruby on Rails。然而,MVC框架的过滤器是完全不同的ASP.NET平台的Request.Filterand的Response.Filter对象,执行转换请求和响应流(一种先进的和经常进行的活动)。您可以使用Request.Filter和Response.Filterin MVC应用程序。 使用过滤器 当我们申请授权的行动SportsStore的AdminController控制器的方法。我们希望的操作方法只能用于用户身份验证自己的人,这为我们提供了一个可供选择的方法。我们可以已检查的授权状况,在每一个要求,每一个动作方法,如下面:
View Code
public class AdminController : Controller { private IProductRepository repository; public AdminController(IProductRepository repo) { this.repository = repo; } /// <summary> /// 展示商品列表页面 /// </summary> /// <returns>商品的列表</returns> public ViewResult Index() { return this.View(this.repository.Products); } /// <summary> /// 编辑方法 /// </summary> /// <param name="productId">商品的ID</param> /// <returns>编辑后的商品</returns> public ViewResult Edit(int productId) { Product product = this.repository.Products.FirstOrDefault(h => h.ProductID == productId); return this.View(product); } public ActionResult Edit(Product product, HttpPostedFileBase image) { if (ModelState.IsValid) { if (image != null) { product.ImageMimeType = image.ContentType; product.ImageData = new byte[image.ContentLength]; image.InputStream.Read(product.ImageData, 0, image.ContentLength); } this.repository.SaveProduct(product); //设置临时字典 TempData["message"] = string.Format("{0} Has Been Saved", product.Name); return this.View(product); //.RedirectToAction("Index"); } else { //如果数据有问题 return this.View(product); } } } 可以看到代码里有大量的重复的东东,在这种方法中,这就是为什么我们决定使用一个过滤器代替,所以上面的代码可以修改为下面:
View Code
[Authorize] public class AdminController : Controller { private IProductRepository repository; public AdminController(IProductRepository repo) { this.repository = repo; } /// <summary> /// 展示商品列表页面 /// </summary> /// <returns>商品的列表</returns> public ViewResult Index() { return this.View(this.repository.Products); } /// <summary> /// 编辑方法 /// </summary> /// <param name="productId">商品的ID</param> /// <returns>编辑后的商品</returns> public ViewResult Edit(int productId) { Product product = this.repository.Products.FirstOrDefault(h => h.ProductID == productId); return this.View(product); } [HttpPost] public ActionResult Edit(Product product, HttpPostedFileBase image) { if (ModelState.IsValid) { if (image != null) { product.ImageMimeType = image.ContentType; product.ImageData = new byte[image.ContentLength]; image.InputStream.Read(product.ImageData, 0, image.ContentLength); } this.repository.SaveProduct(product); //设置临时字典 TempData["message"] = string.Format("{0} Has Been Saved", product.Name); return this.View(product); //.RedirectToAction("Index"); } else { //如果数据有问题 return this.View(product); } } /// <summary> /// 创建商品 /// </summary> /// <returns>新的商品</returns> public ViewResult Create() { return this.View("Edit", new Product()); } } 过滤器是.NET属性添加额外的步骤,请求处理管道。我们使用了授权过滤清单上面代码中。 介绍四种基本类型的过滤器 MVC框架支持四种不同类型的过滤器。每一个允许您介绍逻辑在请求处理管道的不同点,具体如下表所示:
的MVC框架调用一个动作之前,它会检查该方法的定义,看它是否有实现表中列出的接口的属性。如果是这样,那么在适当的点在请求管道,通过这些接口中定义的方法被调用。该框架包括默认属性类实现过滤器的接口。 应用过滤器在控制器和操作方法 过滤器可以应用到个人的行动方法或整个控制器。在上面代码中,我们采用授权滤波器的AdminController类,它具有相同的效果,将它应用到每个动作在控制器中的方法,修改如下: public class AdminController : Controller { private IProductRepository repository; public AdminController(IProductRepository repo) { this.repository = repo; } /// <summary> /// 展示商品列表页面 /// </summary> /// <returns>商品的列表</returns> [Authorize] public ViewResult Index() { return this.View(this.repository.Products); } /// <summary> /// 编辑方法 /// </summary> /// <param name="productId">商品的ID</param> /// <returns>编辑后的商品</returns> [Authorize] public ViewResult Edit(int productId) { Product product = this.repository.Products.FirstOrDefault(h => h.ProductID == productId); return this.View(product); } [Authorize] [HttpPost] public ActionResult Edit(Product product, HttpPostedFileBase image) { if (ModelState.IsValid) { if (image != null) { product.ImageMimeType = image.ContentType; product.ImageData = new byte[image.ContentLength]; image.InputStream.Read(product.ImageData, 0, image.ContentLength); } this.repository.SaveProduct(product); //设置临时字典 TempData["message"] = string.Format("{0} Has Been Saved", product.Name); return this.View(product); //.RedirectToAction("Index"); } else { //如果数据有问题 return this.View(product); } } } 您可以将多个过滤器,混合和匹配它们的应用。也就是说,他们是否被施加到控制器或一个个别的操作方法。上面三个方法使用的都是一个,只有编辑使用了多个过滤器。 使用授权过滤器 授权过滤器的过滤器首先运行之前,其他类型的过滤器和前动作方法被调用。正如它的名字所暗示的,这些过滤器执行您的授权政策,确保只有经批准的用户可以调用该操作的方法。授权过滤器实现IAuthorizationFilter接口,如下面代码所示:
View Code
namespace MvcFilters.Interface { public interface IAuthorizationFilter { //对使用 AuthorizeAttribute 特性时所需的信息进行封装 void OnAuthorization(AuthorizationContext filterContext); } } 假如 MVC框架已收到请求从浏览器。路由系统已处理请求的URL和提取nameof是有针对性的控制器和动作。控制器类的新实例被创建,但在此之前的操作方法被调用,MVC框架检查,看看是否有任何授权过滤器的操作方法。如果有,则然后IAuthorizationFilter接口,OnAuthorization的定义,唯一的方法被调用。如果认证过滤器,批准该请求,然后进行下一阶段的处理管道。如果没有,那么请求将被拒绝。 创建一个授权认证过滤器 了解如何验证过滤器工作的最好的方法是创建一个简单的例子,。它只是检查,访客先前在(Request.IsAuthenticated的是true)时的记录,在一个固定的允许用户列表中出现的用户名。具体代码如下:
View Code
using System.Web.Mvc; namespace MvcFilters.ContClass { public class CustomAuthAttribute : AuthorizeAttribute { private string[] allowedUsers; //构造函数 public CustomAuthAttribute(params string[] users) { this.allowedUsers = users; } protected override bool AuthorizeCore(HttpContextBase httpContext) { return httpContext.Request.IsAuthenticated && this.allowedUsers.Contains(httpContext.User.Identity.Name, StringComparer.InvariantCultureIgnoreCase); } } } 这个简单的方法创建一个授权过滤器是子类化AuthorizeAttribute类和重写AuthorizeCore方法。这确保了我们到内置的功能中构建在AuthorizeAttribute。 我们的过滤器的构造函数接受一个数组的名字。这些被授权的用户。我们的过滤器包含一个称为PerformAuthenticationCheck的方法,以确保该请求是及认证该用户是授权的集之一。 这个类实现的OnAuthorization方法的。参数传递给这个方法是AuthorizationContext类的一个实例,这是来自ControllerContext。 ControllerContext为我们提供了一些有用的对象的访问权限,至少不是这是一个HttpContextBase,通过它,我们可以访问请求的详细信息的属性。由不同的使用的所有的上下文对象从这个类派生的各种动作过滤器,所以你可以使用这些属性保持一致。如下表:
使用上下文对象,我们可以得到所有的信息,我们需要做出决定的的要求。 AuthorizationContext的定义了两个附加属性,这些属性如表下所示。
第一,ActionDescriptor,这些属性返回一个实例System.Web.Mvc.ActionDescriptor,你可以用它来获取信息的行动,过滤器已被应用。结果,第二个属性,让你的过滤器的工作是关键。但是,如果上下文对象的Result属性设置为一个ActionResult对象,MVC框架将以此作为整个请求的结果。管道中的其余步骤是不执行,并且你已经提供的结果被执行,以产生输出为用户。 使用内置的授权过滤器 MVC框架包含了一个非常有用的内置授权过滤器AuthorizeAttribute。我们可以指定我们的授权政策,使用这个类的两个公共属性,如下表:
使用内置的授权过滤器,代码如下:
View Code
public class HomeController : Controller { // // GET: /Home/ [Authorize(Users="admin,Steve,bob",Roles="admin")] public ActionResult Index() { return this.View(); } } 我们已经指定在列表中的用户和角色。这意味着,授权将不除非两个条件都满足用户的名字是admin,Steve,或bob以及用户的管理的作用。有一个隐含的条件,这是请求进行身份验证。如果我们不指定任何用户或角色,那么任何身份验证的用户可以使用的操作方法。 对于大多数应用,AuthorizeAttribute提供的授权策略,是足够的。如果您要实现一些特别的东西,你可以从这个类派生。这是少得多的风险比实施IAuthorizationFilter接口直接,但你仍然应该非常小心思考通过策略和测试它彻底的影响。 AuthorizeAttribute类提供了两个不同的定制,具体如下描述:
实现自定义授权策略 为了演示如何使用自定义身份验证策略,我们将创建一个自定义AuthorizeAttribute子类。这一政策将授予访问权限的任何人访问该网站从浏览器上直接运行服务器的桌面(Request.IsLocal是真实的),以及远程访问者的用户名和角色匹配的normalAuthorizeAttribute规则。这可能是有益的,允许服务器管理员绕过网站的登录过程。我们可以告诉如果是这样的情况下通过读取的IsLocalproperty,HttpRequestBase类。下面代码演示了我们的自定义过滤器:
View Code
using System.Web; using System.Web.Mvc; namespace MvcFilters.ContClass { public class OrAuthorizationAttribute : AuthorizeAttribute { protected override bool AuthorizeCore(HttpContextBase httpContext) { return httpContext.Request.IsLocal || base.AuthorizeCore(httpContext); } } } 我们可以在之前的Action(方法)上使用我们字自定义的授权,代码如下:
View Code
using System.Web; using System.Web.Mvc; using MvcFilters.ContClass; namespace MvcFilters.Controllers { public class HomeController : Controller { // // GET: /Home/ [OrAuthorization(Users = "admin,Steve,bob", Roles = "admin")] public ActionResult Index() { return this.View(); } } } 现在当地中没有指定名称的列表,并没有被授予了管理员角色的用户将能够使用的操作方法. 实现一个自定义授权失败的政策 处理授权失败尝试的默认策略是将用户重定向到登录页面。我们不要总想做到这一点。例如,如果我们正在使用AJAX,发送重定向可能会导致登录出现在用户正在观看的任何页的中间页。幸运的是,我们可以重写HandleUnauthorizedRequest的AuthorizeAttribute类的方法来创建自定义策略。 我们自定义一个授权失败的策略,代码如下:
View Code
using System.Web; using System.Web.Mvc; namespace MvcFilters.ContClass { public class AjaxAuthorizeAttribute : AuthorizeAttribute { protected override void HandleUnauthorizedRequest(AuthorizationContext context) { if (context.HttpContext.Request.IsAjaxRequest()) { UrlHelper urlHelper = new UrlHelper(context.RequestContext); context.Result = new JsonResult { Data = new { Error = "NotAuthorized", LogOnUrl = urlHelper.Action("LogOn", "Account") }, JsonRequestBehavior = JsonRequestBehavior.AllowGet }; } else { base.HandleUnauthorizedRequest(context); } } } } 使用异常过滤器 如果一个未处理的异常被抛出时调用一个动作异常过滤器只运行方法。唯一的例外,可以从以下位置查找:
创建一个异常过滤器 异常过滤器必须实现IExceptionFilter接口,具体代码如下:
View Code
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Web.Mvc; namespace MvcFilters.Interface { public interface IExceptionFilter { void OnException(ExceptionContext filterContext); } } OnException方法被调用时出现未处理的异常。这个参数方法是一种ExceptionContext对象。这个类别是类似于授权滤波器参数,来自ControllerContext类(所以,你可以得到有关请求的信息)并定义了一些额外的过滤器特定的属性,具体如下表:
被抛出的异常,可通过Exception属性。一个异常过滤器报告说,它已处理异常ExceptionHandled属性设置为true。所有的异常筛选器应用到一个动作被调用,即使这个属性被设置为true,所以它是个好习惯检查是否有另一个过滤器已经处理了这个问题,以避免尝试恢复从另一个过滤器的问题已经解决。 所使用的异常过滤器的Resultproperty告诉MVC框架做什么。这两个异常过滤器的主要用途是记录异常,并显示一个合适的信息给用户。下面代码演示,将用户重定向到一个特定的错误页面时,特定种类出现未处理的异常。
View Code
using System.Web; using System.Web.Mvc; namespace MvcFilters.ContClass { public class MyExceptionAttribute : FilterAttribute, IExceptionFilter { public void OnException(ExceptionContext filterContext) { if (!filterContext.ExceptionHandled && filterContext.Exception is NullReferenceException) { filterContext.Result = new RedirectResult("/SpecialErrorPage.html"); filterContext.ExceptionHandled = true; } } } } 该过滤器NullReferenceException的情况下,如果没有其他异常过滤器处理该异常。我们将用户重定向到一个特殊的错误页面,这对我们使用一个文字网址。如下代码,我们可以应用此过滤器 ... [MyException] public ActionResult Index() { ... 如果该指数的操作方法抛出一个异常,并且该异常的一个实例的NullReferenceException,并没有其他的异常过滤器处理异常,那么我们的过滤器重定向用户的URL到/SpecialErrorPage.html页面的。 使用内置的异常过滤器 HandleErrorAttribute是内置的实施的IExceptionFilter接口和使得它更轻松地创建异常过滤器。有了它,你可以指定一个异常的视图的名称,布局中使用下表中描述的属性。
当遇到未处理的异常的类型指定ExceptionType,这个过滤器将设置HTTP结果代码500(意思是SERVERERROR)和指定的视图渲染视图属性(使用默认的布局或指定的主)。下面代码显示了如何使用HandleErrorAttribute过滤器。 ... [HandleError(ExceptionType=typeof(NullReferenceException), View="SpecialError")] public ActionResult Index() { ... 当渲染一个视图,HandleErrorAttribute过滤器通过HandleErrorInfo的的视图模型对象,这意味着您可以包括详细的异常消息中显示的用户。具体代码如下:
View Code
@Model HandelErrorInfo @{ ViewBag.Title = "Sorry,there was a problem!"; } <p> There was a <b>@Model.Exception.GetType().Name</b> while rendering <b>@Model.ControllerName</b>'s <b>@Model.ActionName</b> action. </p> <p> The exception message is: <b><@Model.Exception.Message></b> </p> <p>Stack trace:</p> <pre>@Model.Exception.StackTrace</pre> 用方法/行动和结果过滤器 动作和结果过滤器是通用的过滤器,可用于任何目的。这两种遵循常见的模式。内置的类创建这些类型的过滤器,IActionFilter,同时实现了接口。下面代码显示了这个接口。
View Code
using System.Text; using System.Web.Mvc; namespace MvcFilters.Interface { public interface IActionFilter { void OnActionExecuting(ActionExecutingContext filterContext); void OnActionExcuted(ActionExecutedContext filterContext); } } 这个接口定义了两个方法。MVC框架调用OnActionExecuting方法之前的操作方法被调用。它调用OnActionExecuted方法后的操作方法被调用。 实现OnActionExecuting方法 OnActionExecuting方法被调用之前调用的操作方法。您可以使用此检查的要求,选择取消请求,修改请求,或启动某些机会活动将跨越调用的动作。该方法的参数是一个
您可以选择性地取消的请求的参数设置Result属性作用的结果.具体代码如下:
View Code
using System.Web; using System.Web.Mvc; namespace MvcFilters.ContClass { public class MyActionFilterAttribute : FilterAttribute, IActionFilter { public void OnActionExecuting(ActionExecutingContext filterContext) { if (!filterContext.HttpContext.Request.IsSecureConnection) { filterContext.Result = new HttpNotFoundResult(); } } public void OnActionExecuted(ActionExecutedContext filterContext) { //...... } } } 上面代码里,我们使用OnActionExecuting方法来检查请求是否已取得使用SSL。如果没有,我们返回一个404 - Not Found响应给用户。 实现OnActionExecuted方法 您还可以使用过滤器来执行一些任务,涵盖了执行的操作方法。下面写一个具体的方法代码:
View Code
using System.Web; using System.Web.Mvc; namespace MvcFilters.ContClass { public class ProfileAttribute : FilterAttribute, IActionFilter { private StopWatch timer; public void OnActionExecuting(ActionExecutingContext filterContext) { timer = Stopwatch.StartNew(); } public void OnActionExecuted(ActionExecutedContext filterContext) { timer.Stop(); if (filterContext.Exception == null) { filterContext.HttpContext.Response.Write( string.Format("Action method elapsed time: {0}", timer.Elapsed.TotalSeconds)); } } } } 我们使用OnActionExecuting方法启动一个定时器(使用Stopwatchtimer System.Diagnostics命名空间中)。在OnActionExecuted方法被调用时,已完成的操作方法。在清单中,我们使用这个方法来停止计时器,写一个消息的响应,报告所用的时间。 被传递到OnActionExecuted方法的参数,该参数是一个ActionExecutedContext对象。这个类定义了一些额外的属性,如下表所示。 Exception属性返回任何异常抛出的操作方法,并ExceptionHandled属性指示是否另一个过滤器处理。
被取消的属性将返回true,如果另一个过滤器已取消的请求(通过设置一个值Result属性),因为过滤器的OnActionExecuting方法被调用的时间。我们的OnActionExecuted方法仍然是所谓的,但只有这样,我们可以清理和释放任何资源,我们所使用的。 实现结果过滤器 动作过滤器和结果过滤器有很多共同点。结果过滤器是采取行动的结果是什么样的动作过滤器的操作方法。结果过滤器实现IResultFilter接口,具体代码如下:
View Code
using System.Text; using System.Web.Mvc; namespace MvcFilters.Interface { public interface IResultFilter { void OnResultExecuting(ResultExecutingContext filterContext); void OnResultExecuted(ResultExecutedContext filterContext); } } 这让我们分开
View Code
using System.Web; using System.Web.Mvc; namespace MvcFilters.ContClass { public class ProfileResultAttribute : FilterAttribute, IResultFilter { private Stopwatch timer; public void OnResultExecuting(ResultExecutingContext filterContext) { timer = Stopwatch.StartNew(); } public void OnResultExecuted(ResultExecutedContext filterContext) { timer.Stop(); filterContext.HttpContext.Response.Write( string.Format("Result execution - elapsed time: {0}", timer.Elapsed.TotalSeconds)); } } } 这个过滤器的时间采取措施执行结果。让我们把过滤器用在来动作方法,具体如下:
View Code
... [ProfileResult] public ActionResult Index() { return View(); } ... 使用内置的行动和结果过滤器类 MVC框架包括一个内置的类,它可以用来创建两个行动和结果过滤器。但与内置的授权和异常过滤器,它不提供任何有用的特性。这个类,叫做ActionFilterAttribute,具体代码如下:
View Code
using System.Web; using System.Web.Mvc; namespace MvcFilters.ContClass { public 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) { } } } 唯一的好处是,你使用这类不需要实现方法,下面代码显示了一个过滤器,ActionFilterAttribute-derived 结合我们的测试的动作方法和行动的结果。
View Code
using System.Web; using System.Web.Mvc; |
请发表评论