首页 > 解决方案 > 获取 ASP.net Core 中的压缩响应长度

问题描述

我已经使用在 Kestrel 上运行的 .net Core 构建了一个 RESTful API。我刚刚使用“GzipCompressionProvider”中间件启用了压缩,如此处所述

我还使用了一个自定义记录器,它将所有请求/响应记录到数据库中,响应和请求长度如下:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    loggerFactory.AddConsole(Configuration.GetSection("Logging"));
    loggerFactory.AddDebug();
    var provider = new FileExtensionContentTypeProvider();
    provider.Mappings[".apk"] = "application/vnd.android.package-archive";
    app.UseCors("AllowAll");
    app.UseResponseCompression();
    app.UseMiddleware<myMiddleware.LoggerMiddleware>();
    app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "default",
            template: "{controller=Home}/{action=Index}/{id?}");
    });            
}

Logger中间件的实现如下:

public LoggerMiddleware(RequestDelegate next, AuditContext dbcontext, IOptions<AppSettings> appsettings, IConfiguration config, ILoggerFactory loggerfactory)
{
    _next = next;
    dbContext = dbcontext;
    appSettings = appsettings;
    _config = config;
    loggerFactory = loggerfactory;
}

public async Task Invoke(HttpContext context)
{
    ApiLogEntry apiLog = null;
    using (MemoryStream requestBodyStream = new MemoryStream())
    {
        using (MemoryStream responseBodyStream = new MemoryStream())
        {
            Stream originalRequestBody = context.Request.Body;
            context.Request.EnableRewind();
            Stream originalResponseBody = context.Response.Body;

            try
            {
                await context.Request.Body.CopyToAsync(requestBodyStream);
                requestBodyStream.Seek(0, SeekOrigin.Begin);

                string requestBodyText = new StreamReader(requestBodyStream).ReadToEnd();

                requestBodyStream.Seek(0, SeekOrigin.Begin);
                context.Request.Body = requestBodyStream;

                string responseBody = "";

                context.Response.Body = responseBodyStream;

                Stopwatch watch = Stopwatch.StartNew();
                await _next(context);
                watch.Stop();

                responseBodyStream.Seek(0, SeekOrigin.Begin);
                responseBody = new StreamReader(responseBodyStream).ReadToEnd();
                string appName = "Tracy";
                if (context.Request.Headers["tracy-app"] != "")
                {
                    appName = context.Request.Headers["tracy-app"];
                }
                string token = "";
                var authorization = context.Request.Headers["Authorization"].ToString();

                if (authorization == "" || !authorization.Contains("Bearer"))
                {
                } else
                {
                    token = authorization.Remove(0, 7).Trim();
                }
                string requestHeaders = string.Join(",", context.Request.Headers.Select(he => he.Key + ":[" + he.Value + "]").ToList());
                string responseHeaders = string.Join(",", context.Response.Headers.Select(he => he.Key + ":[" + he.Value + "]").ToList());

                apiLog = new ApiLogEntry
                {
                    Application = appName,
                    Machine = Environment.MachineName,
                    RequestContentType = context.Request.ContentType,
                    RequestRouteTemplate = context.Request.Path,
                    RequestRouteData = requestBodyText,
                    RequestIpAddress = context.Connection.RemoteIpAddress.MapToIPv4().ToString(),
                    RequestMethod = context.Request.Method,
                    RequestHeaders = requestHeaders,
                    RequestTimestamp = DateTime.Now,
                    RequestUri = (context.Request.IsHttps ? "https://" : "http://") + context.Request.HttpContext.Request.Host.Value + context.Request.Path,
                    ResponseContentType = context.Response.ContentType,
                    ResponseHeaders = responseHeaders,
                    ResponseStatusCode = context.Response.StatusCode,
                    RequestLength = requestBodyText.Length + requestHeaders.Length,
                    ResponseLength = responseBody.Length + responseHeaders.Length,
                    Duration = watch.ElapsedMilliseconds,
                    SimId = context.Request.Headers["sim-serialnumber"],
                    DeviceId = context.Request.Headers["tracy-deviceid"],
                    ClientAppVersion = context.Request.Headers["app-version"],
                    UserId = dws.tracy.Security.AuthHelpers.GetUserId(appSettings, token)
                };

                if (appSettings.Value.Logging.LogResponse)
                {
                    apiLog.ResponseContentBody = responseBody;
                }
                responseBodyStream.Seek(0, SeekOrigin.Begin);

                await responseBodyStream.CopyToAsync(originalResponseBody);
                if (apiLog != null && appSettings.Value.Logging.IsActive)
                {
                    var apilogDB = Mapper.Map<dws.Data.ApiLogEntry>(apiLog);
                    using (var logContext = new AuditContext(_config.GetConnectionString("DwsContext")))
                    {
                        var apiLogRepo = new dws.Data.Repositories.ApiLogEntryRepository(logContext);
                        apiLogRepo.Add(apilogDB);
                        apiLogRepo.Commit();
                    }
                }
            }
            catch (Exception ex)
            {
                //ExceptionLogger.LogToDatabse(ex);
                string innerException = "";
                if (ex.InnerException!=null)
                {
                    innerException = ex.InnerException.Message;
                }

                ILogger logger;
                logger = loggerFactory.CreateLogger("LoggerEntry");
                logger.LogCritical(ex.ToString());
                logger.LogCritical(innerException);

                byte[] data = System.Text.Encoding.UTF8.GetBytes("Server error");
                context.Response.StatusCode = 500;
                originalResponseBody.Write(data, 0, data.Length);

            }
            finally
            {
                context.Request.Body = originalRequestBody;
                context.Response.Body = originalResponseBody;
            }
        }
    }
}

问题是 ResponseLength 始终是预压缩长度。关于我需要更改的任何建议。我假设问题是响应仅在刷新到客户端时才被压缩?在那之后我可以添加我的记录器吗?

标签: asp.netasp.net-coremiddlewarekestrel-http-server

解决方案


问题在这里:

app.UseResponseCompression();
app.UseMiddleware<myMiddleware.LoggerMiddleware>();

使用该顺序,响应压缩中间件将被放置您的中间件之前。这意味着压缩中间件将在您的中间件之前获得请求,但在您的中间件之后处理响应。

尝试在之前添加中间件UseResponseCompression(),您应该会看到压缩内容。

参考:ASP.NET Core 中间件管道


推荐阅读