首页 > 编程语言 >关于asp.net core filters生命周期的探究

关于asp.net core filters生命周期的探究

时间:2023-03-22 14:58:28浏览次数:51  
标签:core asp context filterItem filter filterProviders filters var

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<FilterItem>(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<FilterItem> 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<FilterItem>(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更有心得了。

标签:core,asp,context,filterItem,filter,filterProviders,filters,var
From: https://www.cnblogs.com/guoxiaotian/p/17243907.html

相关文章

  • .NET Core WebApi接口ip限流实践
    .NETCoreWebApi接口ip限流实践前言之前一直想实现接口限流,但一直没去实现,然后刚好看到一篇文章是基于AspNetCoreRateLimit组件的限流策略。这个组件不做多的介绍,想了......
  • Centos安装部署netcore 包含Nginx,Supervisor等
    安装.NETCoreSDK安装libicu依赖yuminstalllibunwindlibicu注册dotnet的repository您需要注册Microsoft签名密钥并添加Microsoft产品提要sudorpm-Uvhhttps://packag......
  • .NET Core 3.0-preview3 发布
    .NETCore3.0Preview3已经发布,框架和ASP.NETCore有许多有趣的更新。这是最重要的更新列表。下载地址:​​https://aka.ms/netcore3download​​。​​.NETCore3.0......
  • .netcore 在Linux(Centos)使用Docker方式部署
    运行环境假设你已经安装好了.netcore运行环境,未配置可以看这篇​​[linux(centos)搭建.netcore运行环境]​​centos:7.2cpu:1核2G内存1M带宽docker:18.06.1-ce安装docke......
  • 10个小技巧助您写出高性能的ASP.NET Core代码
    今天这篇文章我们来聊一聊如何提升并优化ASP.NETCore应用程序的性能,本文的大部分内容来自翻译,当然中间穿插着自己的理解,希望对大家有所帮助!话不多说开始今天的主题吧!我们......
  • netcore强制使用https,把所有的HTTP请求转换为HTTPS
    使用HTTPS根据官方文档(​​https://docs.microsoft.com/en-us/aspnet/core/security/enforcing-ssl?view=aspnetcore-2.1&tabs=visual-studio#require-https​​),它建议A......
  • netcore读取、写入文件内容
    读取文件内容使用System.IO.StreamReader文档地址​​System.IO.StreamReader​​.FileStreamfileStream=newFileStream("file.txt",FileMode.Open);using(StreamRe......
  • 【ASP.NET Core】在node.js上托管Blazor WebAssembly应用
    由于Blazor-WebAssembly是在浏览器中运行的,通常不需要执行服务器代码,只要有个“窝”能托管并提供相关文件的下载即可。所以,当你有一个现成的Blazorwasm项目,没必要用其......
  • CorelDRAW Graphics Suite 2023 套件已经发布啦
    ​2023年3月14日14时,全球知名矢量制图及设计软件,CorelDRAWGraphicsSuite2023中文版(以下简称CorelDRAW2023)新品发布会在苏州举行,本次发布会由思杰马克丁软件有......
  • Framework升级到Core以及Dapper支持达梦数据库
    .NETFramework升级到.NETCore尝试使用try-convert工具没有成功手动处理类库将原有csproj文件内容替换如下<ProjectSdk="Microsoft.NET.Sdk"><PropertyGroup>......