Exemplo n.º 1
0
 public BenchmarkActionFilter(ILogger <BenchmarkActionFilter> logger, NetProOption config, IWebHelper webHelper, IConfiguration configuration, RequestCacheData requestCacheData)
 {
     _logger           = logger;
     _config           = config;
     _webHelper        = webHelper;
     _configuration    = configuration;
     _requestCacheData = requestCacheData;
 }
Exemplo n.º 2
0
        /// <summary>
        /// Post:(从头排序后+body json整体 )hash
        /// </summary>
        /// <param name="context"></param>
        ///  <param name="requestCacheData"></param>
        /// <returns></returns>
        public async Task InvokeAsync(HttpContext context, RequestCacheData requestCacheData, VerifySignOption verifySignOption)
        {
            if (!context.Request.Path.Value.ToLower().StartsWith("/api"))
            {
                await _next(context);

                return;
            }
            var endpoint = context.Features.Get <IEndpointFeature>()?.Endpoint;

            if (endpoint != null)
            {
                if (endpoint.Metadata
                    .Any(m => m is IgnoreSignAttribute))
                {
                    _logger.LogInformation($"{context.Request.Path.Value}路径已绕过签名");
                    await _next(context);

                    return;
                }
            }

            context.Request.EnableBuffering();

            var result = await GetSignValue(context, requestCacheData, verifySignOption);

            if (verifySignOption.IsForce && !result.Item1)
            {
                if (!context?.Response.HasStarted ?? false)
                {
                    context.Response.StatusCode  = StatusCodes.Status403Forbidden;
                    context.Response.ContentType = "application/text";
                }

                await context.Response.WriteAsync(result.Item2);

                //await context.Response.WriteAsync(JsonSerializer.Serialize(new { Code = -1, Msg = $"{result.Item2}", Result = string.Empty }, new JsonSerializerOptions
                //{
                //    PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
                //    Encoder = System.Text.Encodings.Web.JavaScriptEncoder.Create(System.Text.Unicode.UnicodeRanges.All)
                //}));
                await Task.CompletedTask;
                return;
            }
            else
            {
                goto gotoNext;
            }

gotoNext:
            await _next(context);
        }
Exemplo n.º 3
0
        /// <summary>
        /// Post:(从头排序后+body json整体 )hash
        /// </summary>
        /// <param name="context"></param>
        ///  <param name="requestCacheData"></param>
        /// <returns></returns>
        public async Task InvokeAsync(HttpContext context, RequestCacheData requestCacheData, VerifySignOption verifySignOption, IOperationFilter _verifySignCommon)
        {
            if (!context.Request.Path.Value.ToLower().StartsWith("/api"))
            {
                await _next(context);

                return;
            }
            var endpoint = context.Features.Get <IEndpointFeature>()?.Endpoint;

            if (endpoint != null)
            {
                if (endpoint.Metadata
                    .Any(m => m is IgnoreSignAttribute))
                {
                    _logger.LogInformation($"{context.Request.Path.Value}路径已绕过签名;如设置了必须签名依旧没命中,请检查HTTP method是否匹配;Method={context.Request.Method}");
                    await _next(context);

                    return;
                }
            }

            context.Request.EnableBuffering();

            var result = await GetSignValue(context, requestCacheData, verifySignOption, _verifySignCommon);

            if (verifySignOption.IsForce && !result.Item1)
            {
                if (!context?.Response.HasStarted ?? false)
                {
                    context.Response.StatusCode  = StatusCodes.Status403Forbidden;
                    context.Response.ContentType = "application/text";
                }

                await context.Response.WriteAsync(result.Item2);

                await Task.CompletedTask;
                return;
            }
            else
            {
                goto gotoNext;
            }

gotoNext:
            await _next(context);
        }
Exemplo n.º 4
0
        private async Task <Tuple <bool, string> > GetSignValue(HttpContext request, RequestCacheData requestCacheData, VerifySignOption verifySignOption, IOperationFilter _verifySignCommon)
        {
            try
            {
                var convertedDictionatry = request.Request.Query.ToDictionary(s => s.Key, s => s.Value);
                var queryDic             = new Dictionary <string, string>();
                foreach (var item in convertedDictionatry)
                {
                    queryDic.Add(item.Key.ToLower(), item.Value);
                }

                var encryptEnum = EncryptEnum.Default;

                var commonParameters = verifySignOption.CommonParameters;

                if (!queryDic.ContainsKey(commonParameters.TimestampName) || !queryDic.ContainsKey(commonParameters.AppIdName) || !queryDic.ContainsKey(commonParameters.SignName))
                {
                    _logger.LogWarning("方式:[Middleware] url参数中未找到签名所需参数[timestamp];[appid];[EncryptFlag]或[sign]");
                    return(Tuple.Create <bool, string>(false, "签名参数缺失"));
                }

                if (queryDic.ContainsKey(commonParameters.EncryptFlag) && int.TryParse(queryDic[commonParameters.EncryptFlag].ToString(), out int encryptint))
                {
                    encryptEnum = (EncryptEnum)encryptint;
                }
                //认证token也参与签名
                if (!string.IsNullOrWhiteSpace(request.Request.Headers["Authorization"]))
                {
                    queryDic.Add("authorization", request.Request.Headers["Authorization"]);
                }

                var timestampStr = queryDic[commonParameters.TimestampName];
                if (!long.TryParse(timestampStr, out long timestamp) || !SignCommon.CheckTime(timestamp, verifySignOption.ExpireSeconds))
                {
                    _logger.LogWarning($"方式:[Middleware] {timestampStr}时间戳已过期");
                    return(Tuple.Create <bool, string>(false, "请校准客户端时间后再试"));
                }

                var appIdString = queryDic[commonParameters.AppIdName].ToString();
                if (string.IsNullOrEmpty(appIdString))
                {
                    _logger.LogWarning(@"方式:[Middleware] The request parameter is missing the Ak/Sk appID parameter
                                          VerifySign:{
                                            AppSecret:{
                                            [AppId]:[Secret]
                                                      }}");
                    return(Tuple.Create <bool, string>(false, "服务异常,AppIdName未配置"));
                }

                var signvalue = queryDic[commonParameters.SignName].ToString();
                queryDic.Remove(commonParameters.SignName);

                string bodyValue;
                if (requestCacheData == null || string.IsNullOrEmpty(requestCacheData.Body))
                {
                    bodyValue = await SignCommon.ReadAsStringAsync(request);

                    requestCacheData = new RequestCacheData {
                        Body = bodyValue
                    };
                }
                else
                {
                    bodyValue = requestCacheData.Body;
                }

                if (!string.IsNullOrEmpty(bodyValue) && !"null".Equals(bodyValue))
                {
                    bodyValue = Regex.Replace(bodyValue, @"\s(?=([^""]*""[^""]*"")*[^""]*$)", string.Empty);

                    bodyValue = bodyValue.Replace("\r\n", "").Replace(" : ", ":").Replace("\n  ", "").Replace("\n", "").Replace(": ", ":").Replace(", ", ",");

                    queryDic.Add("body", bodyValue);
                }

                var dicOrder = queryDic.OrderBy(s => s.Key, StringComparer.Ordinal).ToList();

                StringBuilder requestStr = new StringBuilder();
                for (int i = 0; i < dicOrder.Count(); i++)
                {
                    if (i == dicOrder.Count() - 1)
                    {
                        requestStr.Append($"{dicOrder[i].Key}={dicOrder[i].Value}");
                    }
                    else
                    {
                        requestStr.Append($"{dicOrder[i].Key}={dicOrder[i].Value}&");
                    }
                }

                var utf8Request = SignCommon.GetUtf8(requestStr.ToString());



                var result = _verifySignCommon.GetSignhHash(utf8Request, _verifySignCommon.GetSignSecret(appIdString), encryptEnum);
                if (verifySignOption.IsDebug)
                {
                    _logger.LogInformation($"方式:[Middleware] 请求接口地址:{request.Request.Path}");
                    _logger.LogInformation($"方式:[Middleware] 服务器签名前重新排序后的值={Convert.ToBase64String(Encoding.Default.GetBytes(utf8Request))}");
                    _logger.LogInformation($"方式:[Middleware] 摘要比对: 服务器签名结果={result}   客户端签名结果={signvalue };equal={signvalue == result}");
                    _logger.LogInformation($"方式:[Middleware] encryptEnum={encryptEnum}密钥={_verifySignCommon.GetSignSecret(appIdString)}");
                }
                else if (signvalue != result)
                {
                    _logger.LogWarning(@$ "方式:[Middleware] 摘要被篡改:[iphide]----{signvalue }
                                            查看详情,请设置VerifySignOption节点的IsDebug为true");
                    return(Tuple.Create <bool, string>(false, "签名异常,请求非法"));
                }
                return(Tuple.Create <bool, string>(true, "签名通过"));
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "方式:[Middleware] 签名异常");
                return(Tuple.Create <bool, string>(false, "签名异常"));
            }
        }
Exemplo n.º 5
0
        /// <summary>
        /// Post:(从头排序后+body json整体 )hash
        /// </summary>
        /// <param name="context"></param>
        /// <param name="responseCacheData">自定义对象不能ctor注入</param>
        /// <returns></returns>
        public async Task InvokeAsync(HttpContext context, ResponseCacheData responseCacheData, RequestCacheData requestCacheData)
        {
            context.Request.EnableBuffering();
            var token = context.RequestAborted.Register(async() =>
            {
                await Task.CompletedTask;
                return;
            });

            var endpoint = context.Features.Get <IEndpointFeature>()?.Endpoint;

            if (endpoint != null)
            {
                if (endpoint.Metadata
                    .Any(m => m is IgnorePostResponseCacheAttribute))
                {
                    goto gotoNext;
                }
            }

            if (context.Request.Method.Equals("get", StringComparison.OrdinalIgnoreCase) ||
                context.Request.Method.Equals("head", StringComparison.OrdinalIgnoreCase) ||
                _memorycache.TryGetValue($"PostResponseCache_{context.Request.Path}", out object _tempIgnoe) ||
                _memorycache.TryGetValue($"IgnorePostResponseCache_{context.Request.Path}", out object _temp))
            {
                goto gotoNext;
            }
            else
            {
                var convertedDictionatry = context.Request.Query.ToDictionary(s => s.Key.ToLower(), s => s.Value);

                foreach (var item in _responseCacheOption?.IgnoreVaryQuery ?? new List <string>())
                {
                    if (convertedDictionatry.ContainsKey(item.ToLower()))
                    {
                        convertedDictionatry.Remove(item.ToLower());
                    }
                }

                StringBuilder requestStrKey = new StringBuilder(context.Request.Path);
                foreach (var item in convertedDictionatry)
                {
                    requestStrKey.Append($"{item.Key}{item.Value}");
                }

                string bodyValue;
                if (requestCacheData == null || string.IsNullOrEmpty(requestCacheData.Body))
                {
                    bodyValue = await ReadAsString(context);

                    requestCacheData = new RequestCacheData {
                        Body = bodyValue
                    };
                }
                else
                {
                    bodyValue = requestCacheData.Body;
                }
                if (!string.IsNullOrEmpty(bodyValue) && !"null".Equals(bodyValue))
                {
                    //非Get请求body有值才被缓存,其他默认不缓存,防止body读取失败导致缓存异常
                    bodyValue = Regex.Replace(bodyValue, @"\s(?=([^""]*""[^""]*"")*[^""]*$)", string.Empty);
                    bodyValue = bodyValue.Replace("\r\n", "").Replace(" : ", ":").Replace("\n  ", "").Replace("\n", "").Replace(": ", ":").Replace(", ", ",");

                    requestStrKey.Append($"body{bodyValue}");

                    var cacheResponseBody = _memorycache.Get <ResponseCacheData>($"NetProPostResponse:{requestStrKey}");
                    if (cacheResponseBody != null && !context.RequestAborted.IsCancellationRequested)
                    {
                        //https://stackoverflow.com/questions/45675102/asp-net-core-middleware-cannot-set-status-code-on-exception-because-response-ha
                        if (!context.Response.HasStarted)
                        {
                            context.Response.StatusCode  = cacheResponseBody.StatusCode;
                            context.Response.ContentType = cacheResponseBody.ContentType;
                            await context.Response.WriteAsync(cacheResponseBody.Body);

                            _iLogger.LogInformation($"触发PostResponseCacheMiddleware本地缓存");
                        }
                        else
                        {
                            _iLogger.LogError($"StatusCode无法设置,因为响应已经启动,位置为:触发本地缓存开始赋值[responsecache2]");
                            await Task.CompletedTask;
                            return;
                        }
                    }
                    else if (!context.RequestAborted.IsCancellationRequested)
                    {
                        Stream originalBody = context.Response.Body;
                        try
                        {
                            using (var memStream = new MemoryStream())
                            {
                                context.Response.Body = memStream;

                                await _next(context);

                                memStream.Position = 0;
                                string responseBody = new StreamReader(memStream).ReadToEnd();
                                responseCacheData = new ResponseCacheData
                                {
                                    Body        = responseBody,
                                    ContentType = context.Response.ContentType,
                                    StatusCode  = context.Response.StatusCode
                                };
                                memStream.Position = 0;
                                await memStream.CopyToAsync(originalBody);

                                _memorycache.GetOrCreate <ResponseCacheData>($"NetProPostResponse:{requestStrKey}", s =>
                                {
                                    s.AbsoluteExpirationRelativeToNow = new TimeSpan(_responseCacheOption.Duration);
                                    return(new ResponseCacheData
                                    {
                                        Body = responseBody,
                                        ContentType = context.Response.ContentType,
                                        StatusCode = context.Response.StatusCode
                                    });
                                });
                            }
                            await Task.CompletedTask;
                            return;
                        }
                        finally
                        {
                            context.Response.Body = originalBody;
                        }
                    }
                    else if (context.RequestAborted.IsCancellationRequested)
                    {
                        await Task.CompletedTask;
                        return;
                    }
                }
                else if (!context.RequestAborted.IsCancellationRequested)
                {
                    goto gotoNext;
                }
                else
                {
                    await Task.CompletedTask;
                    return;
                }
            }

gotoNext:
            await _next(context);
        }
        /// <summary>
        ///
        /// </summary>
        /// <param name="context"></param>
        /// <param name="next"></param>
        /// <returns></returns>
        public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
        {
            IServiceProvider serviceProvider = context.HttpContext.RequestServices;
            var responseCacheOption          = serviceProvider.GetService <ResponseCacheOption>();

            //配置存在且配置关闭或者持续时长等于0,即忽略响应缓存
            if ((responseCacheOption != null && !responseCacheOption.Enabled) || Duration == 0)
            {
                goto gotoNext;
            }
            context.HttpContext.Request.EnableBuffering();
            var           _logger        = serviceProvider.GetRequiredService <ILogger <PostResponseCacheAttribute> >();
            var           _configuration = serviceProvider.GetRequiredService <IConfiguration>();
            var           _memorycache   = serviceProvider.GetService <IMemoryCache>();
            IRedisManager _redisManager  = null;

            if ((responseCacheOption != null && responseCacheOption.Cluster) || Cluster)
            {
                _redisManager = serviceProvider.GetService <IRedisManager>();
                if (_redisManager == null)
                {
                    throw new ArgumentNullException(nameof(IMemoryCache), "Post响应已启用集群模式,缓存依赖IRedisManager,请services.AddRedisManager()注入IRedisManager后再使用[PostResponseCache]");
                }
            }
            if (_memorycache == null)
            {
                throw new ArgumentNullException(nameof(IMemoryCache), "Post响应缓存依赖IMemoryCache,请services.AddMemoryCache()注入IMemoryCache后再使用[PostResponseCache]");
            }
            //标识已触发过响应缓存,防止中间件再次触发
            _memorycache.Set($"PostResponseCache_{context.HttpContext.Request.Path}", "标识已触发过响应缓存,防止中间件再次触发", new MemoryCacheEntryOptions {
                AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5)
            });

            var _requestCacheData = serviceProvider.GetService <RequestCacheData>();

            if (_requestCacheData == null)
            {
                throw new ArgumentNullException(nameof(IMemoryCache), "Post响应缓存依赖ResponseCacheData,请调用services.AddShareRequestBody()注入后再使用[PostResponseCache]");
            }

            var token = context.HttpContext.RequestAborted.Register(async() =>
            {
                await Task.CompletedTask;
                return;
            });

            var descriptor = (Microsoft.AspNetCore.Mvc.Controllers.ControllerActionDescriptor)context.ActionDescriptor;

            var attribute = (IgnorePostResponseCacheAttribute)descriptor.MethodInfo.GetCustomAttributes(typeof(IgnorePostResponseCacheAttribute), true).FirstOrDefault();

            if (attribute != null)
            {
                //记录忽略的路由,中间件跳过
                _memorycache.Set($"IgnorePostResponseCache_{context.HttpContext.Request.Path}", true, new MemoryCacheEntryOptions {
                    AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5)
                });
                goto gotoNext;
            }

            if (context.HttpContext.Request.Method.Equals("get", StringComparison.OrdinalIgnoreCase) || context.HttpContext.Request.Method.Equals("head", StringComparison.OrdinalIgnoreCase))
            {
                goto gotoNext;
            }
            else
            {
                var convertedDictionatry = context.HttpContext.Request.Query.ToDictionary(s => s.Key.ToLower(), s => s.Value);

                if (IgnoreVaryByQueryKeys == null || !IgnoreVaryByQueryKeys.Any())
                {
                    if (responseCacheOption.IgnoreVaryByQueryKeys.Any())
                    {
                        IgnoreVaryByQueryKeys = responseCacheOption.IgnoreVaryByQueryKeys.ToArray();
                    }
                }

                foreach (var item in IgnoreVaryByQueryKeys ?? new string[0])
                {
                    if (convertedDictionatry.ContainsKey(item.ToLower()))
                    {
                        convertedDictionatry.Remove(item.ToLower());
                    }
                }

                StringBuilder requestStrKey = new StringBuilder(context.HttpContext.Request.Path);
                foreach (var item in convertedDictionatry)
                {
                    requestStrKey.Append($"{item.Key}{item.Value}");
                }

                string bodyValue;
                if (_requestCacheData == null || string.IsNullOrEmpty(_requestCacheData.Body))
                {
                    bodyValue = await Common.ReadAsString(context.HttpContext);

                    _requestCacheData = new RequestCacheData {
                        Body = bodyValue
                    };
                }
                else
                {
                    bodyValue = _requestCacheData.Body;
                }
                if (!string.IsNullOrEmpty(bodyValue) && !"null".Equals(bodyValue))
                {
                    //非Get请求body有值才被缓存,其他默认不缓存,防止body读取失败导致缓存异常
                    bodyValue = Regex.Replace(bodyValue, @"\s(?=([^""]*""[^""]*"")*[^""]*$)", string.Empty);
                    bodyValue = bodyValue.Replace("\r\n", "").Replace(" : ", ":").Replace("\n  ", "").Replace("\n", "").Replace(": ", ":").Replace(", ", ",");

                    requestStrKey.Append($"body{bodyValue}");
                    ResponseCacheData cacheResponseBody = null;
                    if (Cluster)
                    {
                        cacheResponseBody = _redisManager.Get <ResponseCacheData>($"NetProPostResponse:{requestStrKey}");
                    }
                    else
                    {
                        cacheResponseBody = _memorycache.Get <ResponseCacheData>($"NetProPostResponse:{requestStrKey}");
                    }

                    if (cacheResponseBody != null && !context.HttpContext.RequestAborted.IsCancellationRequested)
                    {
                        if (!context.HttpContext.Response.HasStarted)
                        {
                            _logger.LogInformation($"触发PostResponseCacheAttribute本地缓存");
                            switch (ResponseMode)
                            {
                            case ResponseMode.Cache:
                                context.HttpContext.Response.StatusCode  = cacheResponseBody.StatusCode;
                                context.HttpContext.Response.ContentType = cacheResponseBody.ContentType;
                                await context.HttpContext.Response.WriteAsync(cacheResponseBody.Body);

                                await Task.CompletedTask;
                                return;

                            case ResponseMode.Error:
                                if (cacheResponseBody.StatusCode == 200)
                                {
                                    //TODO确定StatusCode与Headers响应的先后顺序
                                    context.HttpContext.Response.StatusCode = (int)HttpStatusCode.Conflict;
                                    context.HttpContext.Response.Headers.Add("Kestrel-ResponseMode", $"{ResponseMode}");
                                    context.HttpContext.Response.ContentType = cacheResponseBody.ContentType;
                                    await context.HttpContext.Response.WriteAsync(JsonSerializer.Serialize(new
                                    {
                                        Code = -1,
                                        Msg  = $"{Message}"
                                    }, new JsonSerializerOptions
                                    {
                                        PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
                                        Encoder = System.Text.Encodings.Web.JavaScriptEncoder.Create(System.Text.Unicode.UnicodeRanges.All)
                                    }), Encoding.UTF8);

                                    await Task.CompletedTask;
                                    return;
                                }
                                else
                                {
                                    context.HttpContext.Response.StatusCode  = cacheResponseBody.StatusCode;
                                    context.HttpContext.Response.ContentType = cacheResponseBody.ContentType;
                                    await context.HttpContext.Response.WriteAsync(cacheResponseBody.Body);

                                    await Task.CompletedTask;
                                    return;;
                                }
                            }
                        }
                        else
                        {
                            _logger.LogError($"StatusCode无法设置,因为响应已经启动,位置为:触发本地缓存开始赋值[responsecache2]");
                            await Task.CompletedTask;
                            return;
                        }
                    }
                    else if (!context.HttpContext.RequestAborted.IsCancellationRequested)
                    {
                        try
                        {
                            var actionResult = await next();

                            dynamic responseResult = (dynamic)actionResult.Result;
                            if (actionResult.Exception != null)
                            {
                                // 过滤器中await next();执行的始终是Action而不是下一个过滤器或者中间件
                                await Task.CompletedTask;
                                return;
                            }
                            if (responseResult == null)
                            {
                                await Task.CompletedTask;
                                return;
                            }

                            if (responseResult.GetType().Name == "EmptyResult")
                            {
                                await Task.CompletedTask;
                                return;
                            }

                            string body = string.Empty;
                            if (responseResult as Microsoft.AspNetCore.Mvc.ObjectResult != null)
                            {
                                body = JsonSerializer.Serialize(responseResult.Value, new JsonSerializerOptions
                                {
                                    PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
                                    Encoder = System.Text.Encodings.Web.JavaScriptEncoder.Create(System.Text.Unicode.UnicodeRanges.All)
                                });
                            }

                            if (Cluster)
                            {
                                _redisManager.Set($"NetProPostResponse:{requestStrKey}", new ResponseCacheData
                                {
                                    Body        = body,
                                    ContentType = "application/json",
                                    StatusCode  = responseResult.StatusCode ?? 200,
                                }, TimeSpan.FromSeconds(Duration));
                            }
                            else
                            {
                                _memorycache.Set($"NetProPostResponse:{requestStrKey}", new ResponseCacheData
                                {
                                    Body        = body,
                                    ContentType = "application/json",
                                    StatusCode  = responseResult.StatusCode ?? 200,
                                }, TimeSpan.FromSeconds(Duration));
                            }
                        }
                        catch (Exception ex)
                        {
                            await Task.CompletedTask;
                            return;
                        }
                        await Task.CompletedTask;
                        return;
                    }
                    else if (context.HttpContext.RequestAborted.IsCancellationRequested)
                    {
                        await Task.CompletedTask;
                        return;
                    }
                }
                else if (!context.HttpContext.RequestAborted.IsCancellationRequested)
                {
                    goto gotoNext;
                }
                else
                {
                    await Task.CompletedTask;
                    return;
                }
            }

gotoNext:
            await next();
        }
Exemplo n.º 7
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="context"></param>
        /// <param name="next"></param>
        /// <returns></returns>
        public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
        {
            IServiceProvider serviceProvider = context.HttpContext.RequestServices;
            var responseCacheOption          = serviceProvider.GetService <ResponseCacheOption>();

            if (responseCacheOption == null || Duration == 0 || !responseCacheOption.Enabled)
            {
                goto gotoNext;
            }
            context.HttpContext.Request.EnableBuffering();
            var _logger        = serviceProvider.GetRequiredService <ILogger <PostResponseCacheAttribute> >();
            var _configuration = serviceProvider.GetRequiredService <IConfiguration>();
            var _memorycache   = serviceProvider.GetService <IMemoryCache>();

            if (_memorycache == null)
            {
                throw new ArgumentNullException(nameof(IMemoryCache), "Post响应缓存依赖IMemoryCache,请services.AddMemoryCache()注入IMemoryCache后再使用[PostResponseCache]");
            }
            _memorycache.Set($"PostResponseCache_{context.HttpContext.Request.Path}", true);

            var _requestCacheData = serviceProvider.GetService <RequestCacheData>();

            if (_requestCacheData == null)
            {
                throw new ArgumentNullException(nameof(IMemoryCache), "Post响应缓存依赖ResponseCacheData,请调用services.AddShareRequestBody()注入后再使用[PostResponseCache]");
            }

            var token = context.HttpContext.RequestAborted.Register(async() =>
            {
                await Task.CompletedTask;
                return;
            });

            var descriptor = (Microsoft.AspNetCore.Mvc.Controllers.ControllerActionDescriptor)context.ActionDescriptor;

            var attribute = (IgnorePostResponseCacheAttribute)descriptor.MethodInfo.GetCustomAttributes(typeof(IgnorePostResponseCacheAttribute), true).FirstOrDefault();

            if (attribute != null)
            {
                _memorycache.Set($"IgnorePostResponseCache_{context.HttpContext.Request.Path}", true);
                goto gotoNext;
            }

            if (context.HttpContext.Request.Method.Equals("get", StringComparison.OrdinalIgnoreCase) || context.HttpContext.Request.Method.Equals("head", StringComparison.OrdinalIgnoreCase))
            {
                goto gotoNext;
            }
            else
            {
                var convertedDictionatry = context.HttpContext.Request.Query.ToDictionary(s => s.Key.ToLower(), s => s.Value);

                foreach (var item in IgnoreVaryByQueryKeys ?? new string[0])
                {
                    if (convertedDictionatry.ContainsKey(item.ToLower()))
                    {
                        convertedDictionatry.Remove(item.ToLower());
                    }
                }

                StringBuilder requestStrKey = new StringBuilder(context.HttpContext.Request.Path);
                foreach (var item in convertedDictionatry)
                {
                    requestStrKey.Append($"{item.Key}{item.Value}");
                }

                string bodyValue;
                if (_requestCacheData == null || string.IsNullOrEmpty(_requestCacheData.Body))
                {
                    bodyValue = await ReadAsString(context.HttpContext, _logger);

                    _requestCacheData = new RequestCacheData {
                        Body = bodyValue
                    };
                }
                else
                {
                    bodyValue = _requestCacheData.Body;
                }
                if (!string.IsNullOrEmpty(bodyValue) && !"null".Equals(bodyValue))
                {
                    //非Get请求body有值才被缓存,其他默认不缓存,防止body读取失败导致缓存异常
                    bodyValue = Regex.Replace(bodyValue, @"\s(?=([^""]*""[^""]*"")*[^""]*$)", string.Empty);
                    bodyValue = bodyValue.Replace("\r\n", "").Replace(" : ", ":").Replace("\n  ", "").Replace("\n", "").Replace(": ", ":").Replace(", ", ",");

                    requestStrKey.Append($"body{bodyValue}");

                    var cacheResponseBody = _memorycache.Get <ResponseCacheData>($"NetProPostResponse:{requestStrKey}");
                    if (cacheResponseBody != null && !context.HttpContext.RequestAborted.IsCancellationRequested)
                    {
                        if (!context.HttpContext.Response.HasStarted)
                        {
                            _logger.LogInformation($"触发PostResponseCacheAttribute本地缓存");
                            context.HttpContext.Response.StatusCode  = cacheResponseBody.StatusCode;
                            context.HttpContext.Response.ContentType = cacheResponseBody.ContentType;
                            await context.HttpContext.Response.WriteAsync(cacheResponseBody.Body);

                            await Task.CompletedTask;
                            return;
                        }
                        else
                        {
                            _logger.LogError($"StatusCode无法设置,因为响应已经启动,位置为:触发本地缓存开始赋值[responsecache2]");
                            await Task.CompletedTask;
                            return;
                        }
                    }
                    else if (!context.HttpContext.RequestAborted.IsCancellationRequested)
                    {
                        try
                        {
                            var actionResult = await next();

                            dynamic responseResult = (dynamic)actionResult.Result;
                            if (actionResult.Exception != null)
                            {
                                // 过滤器中await next();执行的始终是Action而不是下一个过滤器或者中间件
                                await Task.CompletedTask;
                                return;
                            }
                            if (responseResult == null)
                            {
                                await Task.CompletedTask;
                                return;
                            }
                            var body = JsonSerializer.Serialize(responseResult.Value, new JsonSerializerOptions
                            {
                                PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
                                Encoder = System.Text.Encodings.Web.JavaScriptEncoder.Create(System.Text.Unicode.UnicodeRanges.All)
                            });
                            _memorycache.Set($"NetProPostResponse:{requestStrKey}", new ResponseCacheData
                            {
                                Body        = body,
                                ContentType = "application/json",
                                StatusCode  = responseResult.StatusCode
                            }, TimeSpan.FromSeconds(Duration));
                        }
                        catch (Exception)
                        {
                            await Task.CompletedTask;
                            return;
                        }
                        await Task.CompletedTask;
                        return;
                    }
                    else if (context.HttpContext.RequestAborted.IsCancellationRequested)
                    {
                        await Task.CompletedTask;
                        return;
                    }
                }
                else if (!context.HttpContext.RequestAborted.IsCancellationRequested)
                {
                    goto gotoNext;
                }
                else
                {
                    await Task.CompletedTask;
                    return;
                }
            }

gotoNext:
            await next();
        }
Exemplo n.º 8
0
        /// <summary>
        /// Post:(从头排序后+body json整体 )hash
        /// </summary>
        /// <param name="context"></param>
        /// <param name="responseCacheData">自定义对象不能ctor注入</param>
        /// <returns></returns>
        public async Task InvokeAsync(HttpContext context, ResponseCacheData responseCacheData, RequestCacheData requestCacheData)
        {
            context.Request.EnableBuffering();
            var token = context.RequestAborted.Register(async() =>
            {
                await Task.CompletedTask;
                return;
            });

            var endpoint = context.GetEndpoint();

            if (endpoint != null)
            {
                if (endpoint.Metadata
                    .Any(m => m is IgnorePostResponseCacheAttribute))
                {
                    goto gotoNext;
                }
            }

            if (context.Request.Method.Equals("get", StringComparison.OrdinalIgnoreCase) ||
                context.Request.Method.Equals("head", StringComparison.OrdinalIgnoreCase) ||
                _memorycache.TryGetValue($"PostResponseCache_{context.Request.Path}", out object _tempIgnoe) ||
                _memorycache.TryGetValue($"IgnorePostResponseCache_{context.Request.Path}", out object _temp))
            {
                goto gotoNext;
            }
            else
            {
                var convertedDictionatry = context.Request.Query.ToDictionary(s => s.Key.ToLower(), s => s.Value);

                foreach (var item in _responseCacheOption?.IgnoreVaryByQueryKeys ?? new List <string>())
                {
                    if (convertedDictionatry.ContainsKey(item.ToLower()))
                    {
                        convertedDictionatry.Remove(item.ToLower());
                    }
                }

                StringBuilder requestStrKey = new StringBuilder(context.Request.Path);
                foreach (var item in convertedDictionatry)
                {
                    requestStrKey.Append($"{item.Key}{item.Value}");
                }

                string bodyValue;
                if (requestCacheData == null || string.IsNullOrEmpty(requestCacheData.Body))
                {
                    bodyValue = await Common.ReadAsString(context);

                    requestCacheData = new RequestCacheData {
                        Body = bodyValue
                    };
                }
                else
                {
                    bodyValue = requestCacheData.Body;
                }
                if (!string.IsNullOrEmpty(bodyValue) && !"null".Equals(bodyValue))
                {
                    //非Get请求body有值才被缓存,其他默认不缓存,防止body读取失败导致缓存异常
                    bodyValue = Regex.Replace(bodyValue, @"\s(?=([^""]*""[^""]*"")*[^""]*$)", string.Empty);
                    bodyValue = bodyValue.Replace("\r\n", "").Replace(" : ", ":").Replace("\n  ", "").Replace("\n", "").Replace(": ", ":").Replace(", ", ",");

                    requestStrKey.Append($"body{bodyValue}");
                    ResponseCacheData cacheResponseBody = null;
                    IRedisManager     _redisManager     = null;
                    if (_responseCacheOption.Cluster)
                    {
                        _redisManager = context.RequestServices.GetService <IRedisManager>();
                        if (_redisManager == null)
                        {
                            throw new ArgumentNullException(nameof(RedisCacheOption), $"PostResponseCache组件在集群模式下未检测到注入NetPro.CsRedis组件,请检查是否遗漏{nameof(RedisCacheOption)}配置节点");
                        }
                        cacheResponseBody = _redisManager.Get <ResponseCacheData>($"NetProPostResponse:{requestStrKey}");
                    }
                    else
                    {
                        cacheResponseBody = _memorycache.Get <ResponseCacheData>($"NetProPostResponse:{requestStrKey}");
                    }

                    if (cacheResponseBody != null && !context.RequestAborted.IsCancellationRequested)
                    {
                        //https://stackoverflow.com/questions/45675102/asp-net-core-middleware-cannot-set-status-code-on-exception-because-response-ha
                        if (!context.Response.HasStarted)
                        {
                            context.Response.StatusCode  = cacheResponseBody.StatusCode;
                            context.Response.ContentType = cacheResponseBody.ContentType;
                            context.Response.Headers["response-cache"] = "true";
                            await context.Response.WriteAsync(cacheResponseBody.Body);

                            _iLogger.LogInformation($"触发PostResponseCacheMiddleware本地缓存");
                            //直接return可避免此错误 :OnStarting cannot be set because the response has already started.
                            await Task.CompletedTask;
                            return;
                        }
                        else
                        {
                            _iLogger.LogError($"StatusCode无法设置,因为响应已经启动,位置为:触发本地缓存开始赋值[responsecache2]");
                            await Task.CompletedTask;
                            return;
                        }
                    }
                    else if (!context.RequestAborted.IsCancellationRequested)
                    {
                        Stream originalBody = context.Response.Body;
                        try
                        {
                            using (var memStream = new MemoryStream())
                            {
                                context.Response.Body = memStream;

                                await _next(context);

                                memStream.Position = 0;
                                string responseBody = new StreamReader(memStream).ReadToEnd();
                                responseCacheData = new ResponseCacheData
                                {
                                    Body        = responseBody,
                                    ContentType = context.Response.ContentType,
                                    StatusCode  = context.Response.StatusCode
                                };
                                memStream.Position = 0;
                                await memStream.CopyToAsync(originalBody);

                                if (_responseCacheOption.Cluster)
                                {
                                    _redisManager.Set($"NetProPostResponse:{requestStrKey}", new ResponseCacheData
                                    {
                                        Body        = responseBody,
                                        ContentType = context.Response.ContentType,
                                        StatusCode  = context.Response.StatusCode
                                    }, TimeSpan.FromSeconds(_responseCacheOption.Duration));
                                }
                                else
                                {
                                    _memorycache.Set <ResponseCacheData>($"NetProPostResponse:{requestStrKey}", new ResponseCacheData
                                    {
                                        Body        = responseBody,
                                        ContentType = context.Response.ContentType,
                                        StatusCode  = context.Response.StatusCode
                                    }, TimeSpan.FromSeconds(_responseCacheOption.Duration));
                                }
                            }
                            await Task.CompletedTask;
                            return;
                        }
                        finally
                        {
                            context.Response.Body = originalBody;
                        }
                    }
                    else if (context.RequestAborted.IsCancellationRequested)
                    {
                        await Task.CompletedTask;
                        return;
                    }
                }
                else if (!context.RequestAborted.IsCancellationRequested)
                {
                    goto gotoNext;
                }
                else
                {
                    await Task.CompletedTask;
                    return;
                }
            }

gotoNext:
            await _next(context);
        }