【Web API】客製化ActionFilter執行順序
/
0 Comments
承接上一篇 ActionFilter的執行順序 ,這篇要說明如何客製化ActionFilter的排序來解決執行順序不固定的問題。
在實作前,基本上要對ActionFilter相關類別有些許了解,包含了FilterAttribute、IFilterProvider、FilterInfo、HttpConfiguration及HttpActionDescriptor,這部分本文就不提及了。
首先,你需要為你的Attribute們都加入一個屬性Position來告知你所想要的排序,因此建立一個Interface來提供這個屬性,讓MyAuthorizeAttribute、MyControllerAttribute、MyAction1Attribute及MyAction2Attribute都實作這個interface。
接著要想改掉預設的FilterProvider,需要自己來處理FilterInfo和FilterProvide,新增一個Class CustomFilterInfo並實作IComparable
新增一個class CustomFilterProvider 實作IFilterProvider。值得注意的是,程式中使用FilterScope.Controller是為了將Filter的Scope都設定一致,為了之後設定順序能不受Scope影響。
然後在Global中加入新的FilterProvider並刪除預設的ActionDescriptorFilterProvider。
最後在Controller中把原本的Attribute都加上排序如下。
在跑一次結果,回傳結果確實如你設定的一樣由Position 0跑到3。
參考資料
http://jinyuan.blog.51cto.com/8854733/1548571
在實作前,基本上要對ActionFilter相關類別有些許了解,包含了FilterAttribute、IFilterProvider、FilterInfo、HttpConfiguration及HttpActionDescriptor,這部分本文就不提及了。
首先,你需要為你的Attribute們都加入一個屬性Position來告知你所想要的排序,因此建立一個Interface來提供這個屬性,讓MyAuthorizeAttribute、MyControllerAttribute、MyAction1Attribute及MyAction2Attribute都實作這個interface。
public interface IBaseAttribute { int Position { get; set; } }
接著要想改掉預設的FilterProvider,需要自己來處理FilterInfo和FilterProvide,新增一個Class CustomFilterInfo並實作IComparable
public class CustomFilterInfo : IComparable { public IFilter Instance { get; set; } public FilterScope Scope { get; set; } public CustomFilterInfo(IFilter instance, FilterScope scope) { this.Instance = instance; this.Scope = scope; } public int CompareTo(object obj) { if (obj is CustomFilterInfo) { var item = obj as CustomFilterInfo; if (item.Instance is IBaseAttribute) { var attr = item.Instance as IBaseAttribute; return (this.Instance as IBaseAttribute) .Position.CompareTo(attr.Position); } else { throw new ArgumentException( "Object is of wrong type"); } } else { throw new ArgumentException( "Object is of wrong type"); } } public FilterInfo ConvertToFilterInfo() { return new FilterInfo(this.Instance, this.Scope); } }
新增一個class CustomFilterProvider 實作IFilterProvider。值得注意的是,程式中使用FilterScope.Controller是為了將Filter的Scope都設定一致,為了之後設定順序能不受Scope影響。
public class CustomFilterProvider : IFilterProvider { public IEnumerable<FilterInfo> GetFilters( HttpConfiguration configuration, HttpActionDescriptor actionDescriptor) { IEnumerable<CustomFilterInfo> customActionFilters = actionDescriptor.GetFilters() .Select(i => new CustomFilterInfo( i, FilterScope.Controller)); IEnumerable<CustomFilterInfo> customControllerFilters = actionDescriptor.ControllerDescriptor .GetFilters() .Select(i => new CustomFilterInfo( i, FilterScope.Controller)); return customControllerFilters.Concat(customActionFilters) .OrderBy(i => i) .Select(i => i.ConvertToFilterInfo()); } }
然後在Global中加入新的FilterProvider並刪除預設的ActionDescriptorFilterProvider。
protected void Application_Start() { AreaRegistration.RegisterAllAreas(); GlobalConfiguration.Configure(WebApiConfig.Register); GlobalConfiguration.Configuration.Formatters.XmlFormatter .SupportedMediaTypes.Clear(); //註冊GlobalAttribute GlobalConfiguration.Configuration.Filters.Add(new MyGlobalAttribute()); //加入自訂CustomFilterProvider GlobalConfiguration.Configuration.Services.Add( typeof(System.Web.Http.Filters.IFilterProvider), new CustomFilterProvider()); //刪除預設ActionDescriptorFilterProvider var defaultProvider = GlobalConfiguration.Configuration .Services.GetFilterProviders() .First(x => x is ActionDescriptorFilterProvider); GlobalConfiguration.Configuration.Services.Remove( typeof(System.Web.Http.Filters.IFilterProvider), defaultProvider); }
最後在Controller中把原本的Attribute都加上排序如下。
[MyController(Position = 2)] public class ValuesController : ApiController { [HttpGet] [MyAction2Attribute(Position = 1)] [MyAuthorizeAttribute(Position = 3)] [MyAction1Attribute(Position = 0)] public IEnumerable<Tuple<string, string>> Get() { ...
在跑一次結果,回傳結果確實如你設定的一樣由Position 0跑到3。
參考資料
http://jinyuan.blog.51cto.com/8854733/1548571