【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