C#基礎系列-特性與過濾器


一、前言

   編程中我們會使用特性(Attribute)標注到程序集、類、方法、屬性上進行描述,在Asp.net MVC或者Asp.net WebApi中使用過濾器(Filter)對Action、Result、Exception、Authorize進行AOP(切面編程)。並且過濾器和特性結合,將過濾器優雅的使用在方法上。本文針對特性與過濾器做一個總結。

二、定義

   特性(Attribute),在微軟官方文檔的定義是使用特性,可以有效地將元數據或聲明性信息與代碼(程序集、類型、方法、屬性等)相關聯。按個人理解,就是對元數據的描述,類和方法,屬性作為元數據,使用特性在元數據上使用特性,其作用就是對元數據補充說明,使用反射的方式獲取特性內容,使用特性信息。

  主要用途比如描述類、方法和接口的 COM 屬性、從標題、版本、說明或商標方面描述程序集、描述的方法的安全要求等詳情參考msdn給的官方文檔說明,總結就是基於元數據的描述信息,然后使用描述信息。

  過濾器(Filter),提供了在asp.net MVC與asp.net webApi的請求處理管道過程中注入額外的邏輯,提供了一個簡單而優雅的方式來實現橫切關注點。主要分四類過濾器IAuthorizationFilter(授權過濾器)、IActionFilter(Action方法過濾器)、IResultFilter(ActionResult方法返回結果)、IExceptionFilter(異常過濾器),類庫中通過繼承接口提供了AuthorizeAttribute、ActionFilterAttribute、HandleErrorAttribute給開發者進行繼承重寫實現業務邏輯。

  主要用途比如基於AuthorizeAttribute(提供角色、用戶名)方式的授權、日志信息、安全驗證、圖片防盜鏈、基於ActionFilterAttribute注入業務邏輯,請求和返回結果的處理等。

  在過濾器中.NET FrameWork中提供兩類,一類是在命名空間為using System.Web.Mvc下的提供給ASP.NET MVC的過濾器、一類是在命名空間using System.Web.Http.Filters下提供給ASP.NET WebApi的過濾器,兩者不能混用,否則無法攔截生效。

三、使用

1、創建一個測試用例關於特性和過濾器的解決方案,如下圖所示:

   

// 使用自定義特性在程序集、方法、屬性等上,對其進行描述。通過反射的方式獲取相應信息
using
System; using System.Collections.Generic; using System.Linq; using System.Web; namespace TQF.CustomFilterAttribute.App_Start.Filter { // _Attribute 接口運行時中 public class TestAttribute: Attribute { public string ActionName { get; set; } public TestAttribute() { } } }
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Web;
using System.Web.Mvc;
using TQF.CustomFilterAttribute.App_Start.Filter;

namespace TQF.CustomFilterAttribute.Controllers
{
    /// <summary>
    /// MVC請求
    /// </summary>
    public class HomeController : Controller
    {
        public string Field { get; set; }

        public HomeController()
        {

        }

        //[TestMvcFilter(ActionName =nameof(Index))]
        [Test(ActionName =nameof(Index))]
        public ActionResult Index()
        {
            ViewBag.Title = "Home Page";

            //獲取類的屬性描述
            var classIfno = typeof(HomeController).GetCustomAttribute<TestAttribute>();
            //獲取指定屬性的屬性描述
            var fieldIfno = typeof(HomeController).GetProperty("Field").GetCustomAttribute<TestAttribute>();
            //獲取指定方法的屬性描述
            var methodIfno = typeof(HomeController).GetMethod("Index").GetCustomAttribute<TestAttribute>();

            return View();
        }

        [HttpPost]
        public JsonResult Save()
        {
            //throw (new Exception("error", new InvalidCastException()));
            return new JsonResult();
        }

        public ActionResult Error()
        {
            return View();
        }
    }
}
// 自定義AuthorizeAttribute過濾器
using
System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using TQF.CustomFilterAttribute.Models; namespace TQF.CustomFilterAttribute.App_Start.Filter { /// <summary> /// Mvc請求權限授權過濾器 /// </summary> public class TestMvcAuthorizeAttribute: AuthorizeAttribute { public override void OnAuthorization(AuthorizationContext filterContext) { var result = new ResultModel() { Message = "授權中" }; //actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.OK, result); // 獲取特殊設置的角色和用戶,進行權限判斷 if (Users == "admin") { // 返回,不進行權限驗證或者驗證成功 return; } else { } //base.OnAuthorization(filterContext); } } }
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web;
using System.Web.Http;
using System.Web.Http.Controllers;
using TQF.CustomFilterAttribute.Models;

namespace TQF.CustomFilterAttribute.App_Start.Filter
{
    /// <summary>
    /// webApi請求權限授權過濾器
    /// </summary>
    public class TestHttpAuthorizeAttribute: AuthorizeAttribute
    {
        public override void OnAuthorization(HttpActionContext actionContext)
        {
            var result = new ResultModel() { Message = "授權中" };
            //actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.OK, result);
            // 獲取特殊設置的角色和用戶,進行權限判斷
            if (Users == "admin"){
                // 返回,不進行權限驗證或者驗證成功
                return;
            }else{

            }
            
            base.OnAuthorization(actionContext);
        }
    }
}
// 自定義ActionFilterAttribute過濾器(其包括實現了IActionFilter和IResultFilter接口)
using
System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespace TQF.CustomFilterAttribute.App_Start.Filter { /// <summary> /// Mvc /// </summary> public class TestMvcActionFilterAttribute: ActionFilterAttribute { /// <summary> /// action 執行中 /// </summary> /// <param name="filterContext"></param> public override void OnActionExecuting(ActionExecutingContext filterContext) { filterContext.HttpContext.Response.Write("OnActionExecuting"); base.OnActionExecuting(filterContext); } /// <summary> /// action 執行結束 /// </summary> /// <param name="filterContext"></param> public override void OnActionExecuted(ActionExecutedContext filterContext) { filterContext.HttpContext.Response.Write("OnActionExecuted"); base.OnActionExecuted(filterContext); } /// <summary> /// 生成結果中 /// </summary> /// <param name="filterContext"></param> public override void OnResultExecuting(ResultExecutingContext filterContext) { filterContext.HttpContext.Response.Write("OnResultExecuting"); base.OnResultExecuting(filterContext); } /// <summary> /// /// </summary> /// <param name="filterContext"></param> public override void OnResultExecuted(ResultExecutedContext filterContext) { filterContext.HttpContext.Response.Write("OnResultExecuting"); base.OnResultExecuted(filterContext); } } }
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web;
using System.Web.Http.Controllers;
using System.Web.Http.Filters;
using TQF.CustomFilterAttribute.Models;

namespace TQF.CustomFilterAttribute.App_Start.Filter
{
    /// <summary>
    /// WebApi 請求的Action方法過濾器
    /// </summary>
    public class TestHttpActionFilterAttribute: ActionFilterAttribute
    {
        /// <summary>
        /// 執行前
        /// </summary>
        /// <param name="actionContext"></param>
        public override void OnActionExecuting(HttpActionContext actionContext)
        {
            var result = new ResultModel() { Message = "執行中"};
            // 返回請求結果,不執行后續
            actionContext.Response=actionContext.Request.CreateResponse(HttpStatusCode.OK, result); 
            base.OnActionExecuting(actionContext);
        }

        /// <summary>
        /// 執行后
        /// </summary>
        /// <param name="actionExecutedContext"></param>
        public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
        {
            var result = new ResultModel() { Message = "執行后" };
            // 更改請求結果
            actionExecutedContext.Response = actionExecutedContext.Request.CreateResponse(HttpStatusCode.OK, result);
            base.OnActionExecuted(actionExecutedContext);
        }
    }
}
// 自定義HandleErrorAttribute過濾器
using
System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using TQF.CustomFilterAttribute.Models; namespace TQF.CustomFilterAttribute.App_Start.Filter { /// <summary> /// 處理中產生的異常,異常過濾器 /// </summary> public class TestHandleErrorAttribute: HandleErrorAttribute { public override void OnException(ExceptionContext filterContext) { // 異常的具體信息 var exceptionType = filterContext.Exception.InnerException.GetType().FullName; var exceptionMessage = filterContext.Exception.InnerException.Message; var exceptionMethod = filterContext.Exception.TargetSite; var exceptionStackTrace = filterContext.Exception.StackTrace; // 寫入日志文件 filterContext.ExceptionHandled = true; if (filterContext.HttpContext.Request.IsAjaxRequest()) { // 返回統一的錯誤信息 var result = new ResultModel(); filterContext.HttpContext.Response.ContentType = "application/json"; filterContext.HttpContext.Response.Write(Newtonsoft.Json.JsonConvert.SerializeObject(result)); filterContext.HttpContext.Response.End(); } else { // 返回統一的錯誤頁面 filterContext.HttpContext.Response.Redirect("/Home/Error"); base.OnException(filterContext); filterContext.HttpContext.Response.End(); } } } }
// 過濾器的注冊代碼,通過特性的方式將過濾器標注在Action上,或者在全局中進行注冊,讓所有的Action都使用,比如在Asp.NET MVC中提供的App_Start文件的FilterConfig類中注冊
using
System.Web; using System.Web.Mvc; using TQF.CustomFilterAttribute.App_Start.Filter; namespace TQF.CustomFilterAttribute { public class FilterConfig { public static void RegisterGlobalFilters(GlobalFilterCollection filters) { // filters.Add(new HandleErrorAttribute()); // filters.Add(new TestHandleErrorAttribute()); // filters.Add(new TestMvcActionFilterAttribute()); // filters.Add(new TestMvcAuthorizeAttribute()); // filters.Add(new TestFilter()); } } }
// 在Global.asax的Application_Start方法中GlobalFilters注冊
using
System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Http; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing; using TQF.CustomFilterAttribute.App_Start.Filter; namespace TQF.CustomFilterAttribute { public class WebApiApplication : System.Web.HttpApplication { protected void Application_Start() { //AreaRegistration.RegisterAllAreas(); GlobalConfiguration.Configure(WebApiConfig.Register); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); GlobalFilters.Filters.Add(new TestHttpActionFilterAttribute()); } } }
// 注意ASP.NET WebApi的fliter不能在FilterConfig類中注冊,必須在WebApiConfig類中注冊
using
System; using System.Collections.Generic; using System.Linq; using System.Web.Http; using TQF.CustomFilterAttribute.App_Start.Filter; namespace TQF.CustomFilterAttribute { public static class WebApiConfig { public static void Register(HttpConfiguration config) { // Web API 配置和服務 // Web API 路由 config.MapHttpAttributeRoutes(); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); config.Filters.Add(new TestHttpActionFilterAttribute()); } } }

四、總結

  1、過濾器提供AOP的面向切面的關注點,動態注入業務邏輯,將相關的非主業務邏輯進行優雅解耦,並且可以使用特性的方式便利添加在各個Action上。

  2、通過自定義過濾器,重寫過濾器的虛方法,使用方法中提供的參數上下文信息(包含請求數據的請求體、請求頭、返回數據的請求結果、異常信息)來處理業務。

  3、各種過濾器的執行順序依次是IAuthorizationFilter->IActionFilter->IResultFilter->IExceptionFilter,首先是授權的驗證,其次是Action方法的處理過程和返回結果、最后是產生的異常消息。

  4、對於特性與注釋的區別,注釋是在代碼編譯器編譯階段會丟棄的部分,而特性是代碼的一部分會編譯到程序集(Assembly)的元數據(Metadata)中,在程序運行時候依據元數據反射的方式獲取相應的特性。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM