.Net Core WebAPI IActionFilterによるリクエストキャッシュ

48103 ワード

.Net Core WebAPI IActionFilterによるリクエストキャッシュ
この文書では、Redisキャッシュ方式を使用します.
  • 1新規クラスは、メソッドにタグを付ける必要があるため、まずキャッシュクラスCustomActionCacheAttributeを新規作成し、Attributeを継承する.IActionFilterインタフェース
  • を再参照し実現する.
    public class CustomActionCacheAttribute: Attribute, IActionFilter
    {
         
    	//           
    	public void OnActionExecuting(ActionExecutingContext context)
    	{
         
    		
    	}
        
        //            
        public void OnActionExecuted(ActionExecutedContext context)
        {
         
        	
        }
    }
    
  • 2注入redisキャッシュは.Net coreでは、すべてのキャッシュオブジェクトを操作するインタフェースIDistributedCacheが提供され、using Microsoftを参照する必要がある.Extensions.Caching.Distributedは、クラスで変数を宣言します.
  • 	private IDistributedCache _cache;
    
    	   Filter      ,     ,                 
    	           IDistributedCache   
    
  • 3拡張取得DI注入対象新規静的クラスCustomDIContainerコードは以下の通りである:
  • .
    	/// 
        ///          
        /// 
        public static class CustomDIContainer
        {
         
            private static IHttpContextAccessor _httpContextAccessor;
    
            /// 
            ///     HttpContext
            /// 
            /// 
            /// 
            public static IApplicationBuilder UseCustomHttpContext(this IApplicationBuilder app)
            {
         
                _httpContextAccessor = app.ApplicationServices.GetRequiredService<IHttpContextAccessor>();
                return app;
            }
    
            /// 
            ///     Http   
            /// 
            public static HttpContext Current => _httpContextAccessor.HttpContext;
    
            /// 
            ///   DI     
            /// 
            /// 
            /// 
            public static T GetSerivce<T>() => (T)Current?.RequestServices.GetService(typeof(T));
        }
    

    使用到了IHttpContextAccessor,所以需要在StartupConfigureServices方法中进行http上下文初始化注入.

    public void ConfigureServices(IServiceCollection services)
    {
         
    	......
    	//   NewtonsoftJson  
    	services.AddControllers().AddNewtonsoftJson();
    	//       
        services.AddDistributedRedisCache(r => r.Configuration = Configuration["Redis:ConnectionString"]);
     	//        
     	services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
     	......
    }
    
  • 4キャッシュ拡張キャッシュでは、単に1つのメソッドをキャッシュするだけでなく、メソッドの拡張キャッシュを行うべきであり、メソッドの戻り値のキャッシュを異なるパラメータに基づいて行うため、メソッドのパラメータをシーケンス化する必要がある.方法は以下の通りである:
  • 		/// 
            ///        
            /// 
            /// 
            /// 
            public string GetParams(HttpContext context)
            {
         
                try
                {
         
                    NameValueCollection form = HttpUtility.ParseQueryString(context.Request.QueryString.ToString());
                    HttpRequest request = context.Request;
    
                    string data = string.Empty;
                    switch (request.Method)
                    {
         
                        case "POST":
                            request.Body.Seek(0, SeekOrigin.Begin);
                            using (var reader = new StreamReader(request.Body, Encoding.UTF8))
                            {
         
                                data = reader.ReadToEndAsync().Result;
                                data = data.StringReplaceEmpty("{", "}", "\"", "\'").Replace(":", "=").Replace(",", "&");
                            }
                            break;
                        case "GET":
                            //   :    get  
                            IDictionary<string, string> parameters = new Dictionary<string, string>();
                            for (int f = 0; f < form.Count; f++)
                            {
         
                                string key = form.Keys[f];
                                parameters.Add(key, form[key]);
                            }
    
                            //    :    Key       
                            IDictionary<string, string> sortedParams = new SortedDictionary<string, string>(parameters);
                            IEnumerator<KeyValuePair<string, string>> dem = sortedParams.GetEnumerator();
    
                            //    :              
                            StringBuilder query = new StringBuilder();
                            while (dem.MoveNext())
                            {
         
                                string key = dem.Current.Key;
                                if (!string.IsNullOrEmpty(key))
                                    query.Append(key).Append("=").Append(dem.Current.Value).Append("&");
                            }
                            data = query.ToString().TrimEnd('&');
                            break;
                        default:
                            data = string.Empty;
                            break;
                    }
                    return data;
                }
                catch
                {
         
                    return string.Empty;
                }
            }
    

    并在方法内部声明存储请求参数及方法的字符串

    private string _urlParams;
    
  • 5拡張キャッシュの有効期限は、メソッドごとに結果が返され、異なる更新頻度に応じて異なるキャッシュ時間が設定されます.ヘッダ寸法用に属性を宣言します.
  •         /// 
            ///       [  ]       30  
            /// 
            public int ValidTimeMinutes {
          get; set; } = 30;
    
    • 6 完整代码
    public class CustomActionCacheAttribute : Attribute, IActionFilter
        {
         
            /// 
            ///       [  ]       30  
            /// 
            public int ValidTimeMinutes {
          get; set; } = 30;
    
            private string _urlParams;
    
            private IDistributedCache _cache;
    
            public void OnActionExecuting(ActionExecutingContext context)
            {
         
            	//        DI     IDistributedCache   
                this._cache = CustomDIContainer.GetSerivce<IDistributedCache>();
                //            key
                _urlParams = $"{
           context.HttpContext.Request.Path}?{
           GetParams(context.HttpContext)}";
                //     
                byte[] cacheValue = _cache.Get(_urlParams);
                //         
                if (cacheValue == null || cacheValue.Length == 0) return;
                //        
                // Deserialize      byte[]          
                IActionResult result = cacheValue.Deserialize<string>().Json2Object<ObjectResult>();
                //        ,      context.Result        ,       
                if (result != null)
                    context.Result = result;
            }
    
            public void OnActionExecuted(ActionExecutedContext context)
            {
         
                var result = context.Result.ConvertJson();
                var options = new DistributedCacheEntryOptions().SetSlidingExpiration(TimeSpan.FromMinutes(ValidTimeMinutes));
                // Serialize      String      byte[]     
                this._cache.SetAsync(_urlParams, result.Serialize(), options);
            }
    
            /// 
            ///        
            /// 
            /// 
            /// 
            public string GetParams(HttpContext context)
            {
         
                try
                {
         
                    NameValueCollection form = HttpUtility.ParseQueryString(context.Request.QueryString.ToString());
                    HttpRequest request = context.Request;
    
                    string data = string.Empty;
                    switch (request.Method)
                    {
         
                        case "POST":
                            request.Body.Seek(0, SeekOrigin.Begin);
                            using (var reader = new StreamReader(request.Body, Encoding.UTF8))
                            {
         
                                data = reader.ReadToEndAsync().Result;
                                data = data.StringReplaceEmpty("{", "}", "\"", "\'").Replace(":", "=").Replace(",", "&");
                            }
                            break;
                        case "GET":
                            //   :    get  
                            IDictionary<string, string> parameters = new Dictionary<string, string>();
                            for (int f = 0; f < form.Count; f++)
                            {
         
                                string key = form.Keys[f];
                                parameters.Add(key, form[key]);
                            }
    
                            //    :    Key       
                            IDictionary<string, string> sortedParams = new SortedDictionary<string, string>(parameters);
                            IEnumerator<KeyValuePair<string, string>> dem = sortedParams.GetEnumerator();
    
                            //    :              
                            StringBuilder query = new StringBuilder();
                            while (dem.MoveNext())
                            {
         
                                string key = dem.Current.Key;
                                if (!string.IsNullOrEmpty(key))
                                    query.Append(key).Append("=").Append(dem.Current.Value).Append("&");
                            }
                            data = query.ToString().TrimEnd('&');
                            break;
                        default:
                            data = string.Empty;
                            break;
                    }
                    return data;
                }
                catch
                {
         
                    return string.Empty;
                }
            }
        }
    
    • 7 最后注意
      在.Net Core 3需要配置读取body,在代码中方法执行前和方法执行后中多次使用了HttpContext的Body参数,所以需要在 StartupConfigure方法添加可多次读取。
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
         
    	......
    	app.Use(next => context =>
        {
         
            context.Request.EnableBuffering();
            return next(context);
        });
    	......
    }
    
  • 8使用
  • /// /// /// /// [HttpGet("get_organization")] [CustomActionCache(ValidTimeMinutes = 60 * 24)] public async Task<ExecuteResult<string>> GetOrganizationAsync() { // ...... }
    もし文の中に間違いがあれば、皆さんの指摘を歓迎します.ありがとうございます.転載する必要がある場合は、元の出所を明記してください.