NetCore過濾器

無鹽海發表於2019-04-24

.netcore過濾器有以下幾種型別

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace NetCoreApp.Filters
{
    //優先順序1:許可權過濾器:它在Filter Pipleline中首先執行,並用於決定當前使用者是否有請求許可權。如果沒有請求許可權直接返回。
    public class MyAuthorization : IAuthorizationFilter
    {
        public void OnAuthorization(AuthorizationFilterContext context)
        {

        }
    }
    //優先順序2:資源過濾器: 它在Authorzation後面執行,同時在後面的其它過濾器完成後還會執行。Resource filters 實現快取或其它效能原因返回。因為它執行在模型繫結前,所以這裡的操作都會影響模型繫結。
    public class MyResourceFilterAttribute : IResourceFilter
    {
        //這個ResourceFiltersAttribute是最適合做快取了,在這裡做快取有什麼好處?因為這個OnResourceExecuting是在控制器例項化之前運營,如果能再這裡獲取ViewReuslt就不必例項化控制器,在走一次檢視了,提升效能
        private static readonly Dictionary<string, object> _Cache = new Dictionary<string, object>();
        private string _cacheKey;
        /// <summary>
        /// 這個方法會在控制器例項化之前之前
        /// </summary>
        /// <param name="context"></param>
        public void OnResourceExecuting(ResourceExecutingContext context)
        {
            _cacheKey = context.HttpContext.Request.Path.ToString();//這個是請求地址,它肯定是指向的檢視
            if (_Cache.ContainsKey(_cacheKey))
            {
                var cachedValue = _Cache[_cacheKey] as ViewResult;
                if (cachedValue != null)
                {
                    context.Result = cachedValue;
                }
            }
        }
        /// <summary>
        /// 這個方法是是Action的OnResultExecuted過濾器執行完之後在執行的(每次執行完Action之後得到就是一個ViewResult)
        /// </summary>
        /// <param name="context"></param>
        public void OnResourceExecuted(ResourceExecutedContext context)
        {
            _cacheKey = context.HttpContext.Request.Path.ToString();//這個是請求地址
            if (!string.IsNullOrEmpty(_cacheKey) && !_Cache.ContainsKey(_cacheKey))
            {
                //因為這個方法是是Action的OnResultExecuted過濾器執行完之後在執行的,所以context.Result必然有值了,這個值就是Action方法執行後得到的ViewResult
                var result = context.Result as ViewResult;
                if (result != null)
                {
                    _Cache.Add(_cacheKey, result);
                }
            }
        }
    }
    //優先順序3:方法過濾器:它會在執行Action方法前後被呼叫。這個可以在方法中用來處理傳遞引數和處理方法返回結果。
    public class MyActionFilterAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext context)
        {
            //context.HttpContext.Response.WriteAsync("abc");
        }

    }
    //優先順序4:異常過濾器:被應用全域性策略處理未處理的異常發生前異常被寫入響應體
    public class MyExceptionFilterAttribute:ExceptionFilterAttribute
    {
        private readonly IModelMetadataProvider _moprovider;
        public MyExceptionFilterAttribute(IModelMetadataProvider moprovider)
        {
            this._moprovider = moprovider;
        }
        public override void OnException(ExceptionContext context)
        {
            base.OnException(context);
            if (!context.ExceptionHandled)//如果異常沒有被處理過
            {
                string controllerName = (string)context.RouteData.Values["controller"];
                string actionName = (string)context.RouteData.Values["action"];
                //string msgTemplate =string.Format( "在執行controller[{0}的{1}]方法時產生異常",controllerName,actionName);//寫入日誌

                if (this.IsAjaxRequest(context.HttpContext.Request))
                {

                    context.Result = new JsonResult(new
                    {
                        Result = false,
                        PromptMsg = "系統出現異常,請聯絡管理員",
                        DebugMessage = context.Exception.Message
                    });
                }
                else
                {
                    var result = new ViewResult { ViewName = "~Views/Shared/Error.cshtml" };
                    result.ViewData = new ViewDataDictionary(_moprovider, context.ModelState);
                    result.ViewData.Add("Execption", context.Exception);
                    context.Result = result;
                }
                    
;            }
        }
        //判斷是否為ajax請求
        private bool IsAjaxRequest(HttpRequest request)
        {
            string header = request.Headers["X-Requested-With"];
            return "XMLHttpRequest".Equals(header);
        }
    }
    //優先順序5:結果過濾器:它可以在執行Action結果之前執行,且執行Action成功後執行,使用邏輯必須圍繞view或格式化執行結果。
    public class MyResultFilterAttribute : ResultFilterAttribute
    {
        public override void OnResultExecuting(ResultExecutingContext context)
        {
            base.OnResultExecuting(context);
        }
        public override void OnResultExecuted(ResultExecutedContext context)
        {
            base.OnResultExecuted(context);
        }
    }
}

過濾器的全域性註冊

Startup.cs中

namespace NetCoreApp
{
    public class Startup
    {
        public IConfiguration Configuration { get; } //建構函式注入:Configuration用於讀取配置檔案的
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public void ConfigureServices(IServiceCollection services)
        {
            
            services.Configure<CookiePolicyOptions>(options =>
            {

                options.CheckConsentNeeded = context => true;
                options.MinimumSameSitePolicy = SameSiteMode.None;
            });

            Action<MvcOptions> filters = new Action<MvcOptions>(r=> {
                r.Filters.Add(typeof(MyAuthorization));
                r.Filters.Add(typeof(MyExceptionFilterAttribute));
                r.Filters.Add(typeof(MyResourceFilterAttribute));
                r.Filters.Add(typeof(MyActionFilterAttribute));
                r.Filters.Add(typeof(MyResultFilterAttribute));
            });
            

            services.AddMvc(filters) //註冊全域性過濾器
                .SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

            services.AddAuthentication();
            services.AddSession();       
        }   
    }
}

區域性註冊

我們建立了過濾器,除了註冊這個過濾器為全域性過濾器外,還可以在單個控制器,或者單個方法上使用過濾器

以控制器為列,平常我們直接在在控制器上打上我們的建立的過濾器的特性標籤就好了

[MyActionFilter] //在這裡打上你自己建立的過濾器名稱就好了
public class HomeController : Controller
{
    private IUser _user { get; set; }
    public HomeController(IUser user)//建構函式
    {
        this._user = user;
    }
    public IActionResult Index()
    {
        ViewBag.Now = DateTime.Now.ToString();
        return View();
    }
}

但是,如果你的過濾器中使用了,建構函式注入物件的形式,直接在控制器上打特性標籤就行不通了。(因為特性需要的引數必須是在這個特性構造前就已經寫好了的,不能依賴注入提供的) 

例如:

public class MyActionFilterAttribute : ActionFilterAttribute
{
    private ILogger<MyActionFilterAttribute> _logger;
    public MyActionFilterAttribute(ILogger<MyActionFilterAttribute> logger)
    {
        this._logger = logger;
    }
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        _logger.LogWarning("在這裡執行了Action方法");//記錄日誌
    }
}

這時候我們就需要用另外一種形式對我們的過濾器進行註冊了

方式一:使用TypeFilter這種形式,

[TypeFilter(typeof(MyActionFilterAttribute))] 就表示這個MyActionFilterAttribute這個過濾器物件的建立交給DI來完成。

[TypeFilter(typeof(MyActionFilterAttribute))] //這時候需要這樣使用
public class HomeController : Controller
{
    public IActionResult Index()
    {
        ViewBag.Now = DateTime.Now.ToString();
        return View();
    }
}

方式二:使用ServiceFilter這種形式

[ServiceFilter(typeof(MyActionFilterAttribute))]就表示這個MyActionFilterAttribute這個過濾器物件的建立交給DI來完成

但是這種方式需要多一個配置,就是需要在Startup.cs類中的ConfigureServices方法中新增一段配置程式碼

services.AddScoped(typeof(MyActionFilterAttribute));

這樣就可以在可控制器中使用ServiceFilter了

[ServiceFilter(typeof(MyActionFilterAttribute))]
public class HomeController : Controller
{    
    public IActionResult Index()
    {
        ViewBag.Now = DateTime.Now.ToString();
        return View();
    }
}

 

相關文章