您好,登錄后才能下訂單哦!
怎么在Asp.Net中使用ModelConvention實現全局過濾器隔離?很多新手對此不是很清楚,為了幫助大家解決這個難題,下面小編將為大家詳細講解,有這方面需求的人可以來學習下,希望你能有所收獲。
ModelConvention
ModelConvention定義了操作模型的入口,又或者說是一種契約,通過它我們可以對模型進行修改,常用的Convention包括:
IApplicationModelConvention
IControllerModelConvention
IActionModelConvention
IParameterModelConvention
IPageRouteModelConvention
這些接口提供了一個共同的方法 Apply
,方法參數是各自的應用程序模型,以 IControllerModelConvention
為例看一下它的定義:
namespace Microsoft.AspNetCore.Mvc.ApplicationModels { // // 摘要: // Allows customization of the Microsoft.AspNetCore.Mvc.ApplicationModels.ControllerModel. // // 言論: // To use this interface, create an System.Attribute class which implements the // interface and place it on a controller class. Microsoft.AspNetCore.Mvc.ApplicationModels.IControllerModelConvention // customizations run after Microsoft.AspNetCore.Mvc.ApplicationModels.IApplicationModelConvention // customizations and before Microsoft.AspNetCore.Mvc.ApplicationModels.IActionModelConvention // customizations. public interface IControllerModelConvention { // // 摘要: // Called to apply the convention to the Microsoft.AspNetCore.Mvc.ApplicationModels.ControllerModel. // // 參數: // controller: // The Microsoft.AspNetCore.Mvc.ApplicationModels.ControllerModel. void Apply(ControllerModel controller); } }
從接口摘要可以看到,這個接口允許自定義 ControllerModel
對象,而如何自定義內容正是通過 Apply
方法來實現,這個方法提供了當前 ControllerModel
對象的實例,我們可以在它身上獲取到的東西實在太多了,看看它包含些什么:
有了這些,我們可以做很多很靈活的操作,例如通過設置 ControllerName
字段強制更改控制器的名稱讓程序中寫死的控制器名失效,也可以通過 Filters
字段動態更新它的過濾器集合,通過 RouteValues
來更改路由規則等等。
說到這里,很多人會覺得這玩意兒和自定義過濾器看起來差不多,最開始我也這么認為,但經過實際代碼調試我發現它的生命周期要比過濾器早的多,或者說根本無法比較, 這個家伙只需要在應用啟動時執行一次并不用隨著每次請求而執行 。也就是說,它的執行時間比激活控制器還要早,那時候根本沒有過濾器什么事兒,它的調用是發生在 app.UseEndpoints()
。
回到最開始的需求。基于上面的介紹,我們可以自定義如下的約定:
public class ApiControllerAuthorizeConvention : IControllerModelConvention { public void Apply(ControllerModel controller) { if (controller.Filters.Any(x => x is ApiControllerAttribute) && !controller.Filters.Any(x => x is AccessControlFilter)) { controller.Filters.Add(new AccessControlAttribute()); } } }
上面的主要思路就是通過判斷控制器本身的過濾器集合是否包含 ApiControllerAttribute
來識別是否API Controller,如果是API Controller并且沒有標記過 AccessControlAttribute
的話就新建一個實例加入進去。
那么如何把這個約定注冊到應用中呢?在Microsoft.AspNetCore.Mvc.MvcOptions中提供了 Conventions
屬性:
// // 摘要: // Gets a list of Microsoft.AspNetCore.Mvc.ApplicationModels.IApplicationModelConvention // instances that will be applied to the Microsoft.AspNetCore.Mvc.ApplicationModels.ApplicationModel // when discovering actions. public IList<IApplicationModelConvention> Conventions { get; }
通過操作它就能把自定義約定注入進去:
services.AddMvc(options => { options.Conventions.Add(new ApiControllerAuthorizeConvention()); })
細心的人會發現,Conventions是一個 IApplicationModelConvention
類型的集合,而我們自定義的Convention是一個 IControllerModelConvention
,正常來說應該會報錯才對?原因是Asp.Net Core的DI框架幫我們提供了一系列擴展方法來簡化Convention的添加不用自己再去轉換:
通過代碼調試發現,應用啟動時遍歷了系統中的所有控制器去執行Apply操作,那么通過 IApplicationModelConvention
一樣也能實現這個功能,因為它里面包含了控制器集合:
public class ApiControllerAuthorizeConvention : IApplicationModelConvention { public void Apply(ApplicationModel application) { foreach (var controller in application.Controllers) { if (controller.Filters.Any(x => x is ApiControllerAttribute) && !controller.Filters.Any(x => x is AccessControlFilter)) { controller.Filters.Add(new AccessControlFilter()); } } } }
再改進一下
實際開發中我的AccessControlFilter需要通過構造函數注入業務接口,類似于這樣:
public class AccessControlFilter : IActionFilter { private IUserService _userService; public AccessControlFilter(IUserService service) { _userService = service; } public void OnActionExecuting(ActionExecutingContext context) { //模擬一下業務操作 //var user=_userService.GetById(996); //....... } public void OnActionExecuted(ActionExecutedContext context) { } }
如何優雅的在Convention中使用DI自動注入呢?Asp.Net Core MVC框架提供的 ServiceFilter
可以解決這個問題, ServiceFilter
本身是一個過濾器,它的不同之處在于能夠通過構造函數接收一個Type類型的參數,我們可以在這里把真正要用的過濾器傳進去,于是上面的過濾器注冊過程演變為:
controller.Filters.Add(new ServiceFilterAttribute(typeof(AccessControlFilter)));
當然了,要從DI中獲取這個filter實例,必須要把它注入到DI容器中:
services.AddScoped<AccessControlFilter>();
看完上述內容是否對您有幫助呢?如果還想對相關知識有進一步的了解或閱讀更多相關文章,請關注億速云行業資訊頻道,感謝您對億速云的支持。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。