0%

【WebAPI】Custom ActionFilter Order

ActionAttribute

環境 : WebAPI 2.2 詳細

在WebAPI中,通常都繪有需要客製化ActionFilter的情況發生

執行順序不固定

ActionFilter的執行順序並非在上面的就優先執行,Filter有個FilterScope來表示他屬於何種層級的ActionFilter,執行順序依次是 Global , Controller , Action 。但如果是在同一層級的ActionFilter順序則未必按照固定。

https://2.bp.blogspot.com/-vZ-NniyEom4/VnIx1sGnvcI/AAAAAAAAHro/-ykVRvzvq3Y/s1600/1.png]

測試

https://4.bp.blogspot.com/-LiCgWC_Ob3M/VnIyiMCn0nI/AAAAAAAAHr0/H58PiNpUUvA/s1600/1.png

裡面全部都一樣繼承ActionFilterAttribute即可

1
public class FourAttribute :ActionFilterAttribute{ }

註冊OneAttribute到Global
https://2.bp.blogspot.com/-oCMhVi2M5_w/VnIzKgzvzrI/AAAAAAAAHsA/e9vuB3fiZE4/s1600/1.png

將其它Attribute分別註冊到Controller與Action
https://1.bp.blogspot.com/-VZm61VIoQZY/VnIzc88dwGI/AAAAAAAAHsM/piz8hD4vdXo/s1600/1.png

在Get中補上以下程式然後執行看看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[HttpGet]
[Three]
[Four]
public IEnumerable<Tuple<string,string>> Get()
{
IHttpActionSelector actionSelector =
this.Configuration.Services.GetActionSelector();

HttpActionDescriptor actionDescriptor =
actionSelector.SelectAction(this.ControllerContext);

foreach (FilterInfo filterInfo in actionDescriptor.GetFilterPipeline())
{
yield return new Tuple<string, string>(
filterInfo.Instance.GetType().Name,
filterInfo.Scope.ToString()
);
}
}

測試結果

https://2.bp.blogspot.com/-ExqARkhthR8/VnI0IaZEwvI/AAAAAAAAHsY/iSNXKBQFZF0/s1600/1.png

排序依據所對應的層級,雖然這邊排序與我們在程式上的排序相同。但實際上順序同層級的順序未必是由上而下,為了避免這樣的問題,最好的方法就是在ActionFilter加上排序的屬性來解決。

實作Order屬性

定義IOrderAttribute

1
2
3
4
5
6
7
8
9
public interface IOrderAttribute
{
int Order { get; set; }
}

public class FourAttribute : ActionFilterAttribute, IOrderAttribute
{
public int Order { get; set; }
}

實做IComparable介面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class CustomFilterInfo : IComparable
{
public IFilter Instance { get; set; }
public FilterScope Scope { get; set; }

//FilterInfo
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 IAttribute)
{
var attr = item.Instance as IAttribute;
return (this.Instance as IAttribute).Order.CompareTo(attr.Order);
}
}

return 0;
}

public FilterInfo ConvertToFilterInfo()
{
return new FilterInfo(this.Instance, this.Scope);
}
}

實做IFilterProvider介面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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註冊

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
GlobalConfiguration.Configuration.Formatters.XmlFormatter.SupportedMediaTypes.Clear();

//註冊Global Attribute
GlobalConfiguration.Configuration.Filters.Add(new OneAttribute());


//新增CustomFilterProvider
GlobalConfiguration.Configuration.Services.Add(
typeof(System.Web.Http.Filters.IFilterProvider), new CustomFilterProvider());

var providers = GlobalConfiguration.Configuration.Services.GetFilterProviders();
var defaultprovider = providers.First(i => i is ActionDescriptorFilterProvider);

//移除DefaultProvider
GlobalConfiguration.Configuration.Services.Remove(
typeof(System.Web.Http.Filters.IFilterProvider),
defaultprovider);
}

測試

將Attibute補上Order再執行看看得到的順序

https://1.bp.blogspot.com/-UucLgmkDBeE/VnJTKlpyjVI/AAAAAAAAHso/5-37Ww458bE/s1600/1.png

可以看到Four排在Three的前面了

https://4.bp.blogspot.com/-YndO7jfF4M0/VnJTkNiXDzI/AAAAAAAAHs0/S6HXWfK1_ks/s1600/1.png