最新要闻

广告

手机

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

家电

环球报道:关于asp.net core filters生命周期的探究

来源:博客园

1.背景

昨天看了关于一篇 api 限流的文章,ASP.NET Core WebApi接口限流,作者给出了demo,写的很好,但是我看了一遍,api限流用actionfilterattribute,觉得很奇怪,难道说每次都是用的同一个filter。思考一番觉得自己还是写个demo验证以下,顺便看看源码是如何实现的,


(资料图片)

2.demo

public class MyActionfilterAttribute:ActionFilterAttribute    {        private int a;        public MyActionfilterAttribute()        {            a = 50;        } public override Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)    {            Console.WriteLine($"begin");            ++a;               Console.WriteLine(a.ToString()) ;            Console.WriteLine("end");            return base.OnActionExecutionAsync(context, next);        }    }

调试连续点击多次 出现如下结果,果然是同一个filter。看来确实是可以用actionfilter进行请求限制。

3.源码探究

感觉到奇怪的我转手就去看源码了,不对首先先搜一下有没有相关的文章,找到了一篇https://www.cnblogs.com/xiaoxiaotank/p/15622083.html 详细介绍了filters。点赞。

我的上一篇文章探究了以下controller在什么时侯被构建的,同时什么使用调用filter过滤器管道,其中就有如何获取filter,所以接着继续薅就是了。

ControllerActionInvokerCache

public (ControllerActionInvokerCacheEntry cacheEntry, IFilterMetadata[] filters) GetCachedResult(ControllerContext controllerContext)        {            // We don"t care about thread safety here            if (cacheEntry is null)            {                var filterFactoryResult = FilterFactory.GetAllFilters(_filterProviders, controllerContext);                filters = filterFactoryResult.Filters;                     //省略若干代码            }            else            {                // Filter instances from statically defined filter descriptors + from filter providers                filters = FilterFactory.CreateUncachedFilters(_filterProviders, controllerContext, cacheEntry.CachedFilters);            }            return (cacheEntry, filters);        }

逻辑比较简单,如果没有缓存那么就获取所有的filters,如果有缓存那么就创建没有缓存的filters。

FilterFactory

public static FilterFactoryResult GetAllFilters(            IFilterProvider[] filterProviders,            ActionContext actionContext)        {            var actionDescriptor = actionContext.ActionDescriptor;            var staticFilterItems = new FilterItem[actionDescriptor.FilterDescriptors.Count];            var orderedFilters = actionDescriptor.FilterDescriptors                .OrderBy(                    filter => filter,                    FilterDescriptorOrderComparer.Comparer)                .ToList();            for (var i = 0; i < orderedFilters.Count; i++)            {                staticFilterItems[i] = new FilterItem(orderedFilters[i]);            }            var allFilterItems = new List(staticFilterItems);   // 由filter factory 决定哪个filter可以被缓存            // Execute the filter factory to determine which static filters can be cached.            var filters = CreateUncachedFiltersCore(filterProviders, actionContext, allFilterItems);            // Cache the filter items based on the following criteria            // 1. Are created statically (ex: via filter attributes, added to global filter list etc.)2. Are re-usable        //缓存filter基于以下几点 : 首先静态生成 其次能够被重复使用            var allFiltersAreReusable = true;            for (var i = 0; i < staticFilterItems.Length; i++)            {                var item = staticFilterItems[i];                if (!item.IsReusable)                {                    item.Filter = null;                    allFiltersAreReusable = false;                }            }            if (allFiltersAreReusable && filterProviders.Length == 1 && filterProviders[0] is DefaultFilterProvider defaultFilterProvider)            {                // If we know we can safely cache all filters and only the default filter provider is registered, we can  probably re-use filters between requests.                //如果我们知道我们能够安全的缓存这些filters,然后只有默认的filter providerb被注册,那么我们大   概可以在请求中重复使用这些过滤器                actionDescriptor.CachedReusableFilters = filters;            }            return new FilterFactoryResult(staticFilterItems, filters);        }

看到官方的注释确实我们是重复使用这些 filter的,应该是为了提高提高效率

主要的逻辑是 filterprovider来创建filter,然后我们拿到filter,有个属性为 IsReuable决定了我们是否可以重用这个filter

FilterFactory

private static IFilterMetadata[] CreateUncachedFiltersCore(            IFilterProvider[] filterProviders,            ActionContext actionContext,            List filterItems)        {            // Execute providers            var context = new FilterProviderContext(actionContext, filterItems);            for (var i = 0; i < filterProviders.Length; i++)            {                filterProviders[i].OnProvidersExecuting(context);            }            for (var i = filterProviders.Length - 1; i >= 0; i--)            {                filterProviders[i].OnProvidersExecuted(context);            }            // Extract filter instances from statically defined filters and filter providers//删除一些代码                var filters = new IFilterMetadata[count];                var filterIndex = 0;                for (int i = 0; i < filterItems.Count; i++)                {                    var filter = filterItems[i].Filter;                    if (filter != null)                    {                        filters[filterIndex++] = filter;                    }                }                return filters;        }

进去看看 onprovidersexecuting方法 看就怎么产生的filters,默认的实现是

DefaultFilterProvider

public void OnProvidersExecuting(FilterProviderContext context)        {            if (context.ActionContext.ActionDescriptor.FilterDescriptors != null)            {                var results = context.Results;                // Perf: Avoid allocating enumerator and read interface .Count once rather than per iteration                var resultsCount = results.Count;                for (var i = 0; i < resultsCount; i++)                {                    ProvideFilter(context, results[i]);                }            }        }public void ProvideFilter(FilterProviderContext context, FilterItem filterItem)        {            if (filterItem.Filter != null)  {return;}            var filter = filterItem.Descriptor.Filter;            if (filter is not IFilterFactory filterFactory) //标记的filter不是IFilterFactory类型            {                filterItem.Filter = filter;                filterItem.IsReusable = true; //那么可以重复使用            }            else            {                var services = context.ActionContext.HttpContext.RequestServices;                filterItem.Filter = filterFactory.CreateInstance(services);//创建实例                filterItem.IsReusable = filterFactory.IsReusable;                ApplyFilterToContainer(filterItem.Filter, filterFactory);            }        }

上面的注释就是创建filter的主要逻辑了,如果是静态构造那么可用重复使用,然后IFilterFactory主要有两种类型,一种是ServiceFilterAttribute,要求该过滤器和构造函数参数要在DI容器中注册,另一种是TypeFilterAttribute,部分参数自己提供,部分参数ioc提供。这两种filter.IsReusable=false.

看看如果有缓存那么是如何创建filters

FilterFactory

public static IFilterMetadata[] CreateUncachedFilters(            IFilterProvider[] filterProviders,            ActionContext actionContext,            FilterItem[] cachedFilterItems)        {//删除一些代码            if (actionContext.ActionDescriptor.CachedReusableFilters is { } cached)            {                return cached;  //如果都是可以缓存的filter直接返回            }//深拷贝一份数据            // Deep copy the cached filter items as filter providers could modify them            var filterItems = new List(cachedFilterItems.Length);            for (var i = 0; i < cachedFilterItems.Length; i++)            {                var filterItem = cachedFilterItems[i];                filterItems.Add(                    new FilterItem(filterItem.Descriptor)                    {                        Filter = filterItem.Filter,                        IsReusable = filterItem.IsReusable                    });            }            return CreateUncachedFiltersCore(filterProviders, actionContext, filterItems);        }

深度拷贝了一份数据,但是filterItem.Filter没有拷贝进来,复制的是filterItem.Descriptor属性所以再调用CreateUncachedFiltersCore的时候我们可以复用静态构造的filter,但是其他的就要重新构造一份了。

4.总结

1.静态构造的filter会复用,其他的会重新构造

2.翻了一遍源码,写bug更有心得了。

关键词: