【Web API】客製化ActionFilter執行順序

/
0 Comments
承接上一篇 ActionFilter的執行順序 ,這篇要說明如何客製化ActionFilter的排序來解決執行順序不固定的問題。

在實作前,基本上要對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


You may also like

沒有留言: