Get the compressed response length in ASP.net Core

只愿长相守 提交于 2020-01-04 07:10:21

问题


I've build a RESTful API using .net Core running on Kestrel. I've just enabled compression using "GzipCompressionProvider" middleware as described here.

I'm also using a custom logger that logs all requests/responses to a DB with the response and request lengths as per below:

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?}");
    });            
}

The implementation of the Logger middleware is as follows:

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;
            }
        }
    }
}

The trouble is that the ResponseLength is always the pre-compression length. Any suggestions on what I need to change. I'm assuming the issue is that the response is being compressed only when it is flushed to the client? Am I able to add my logger after that point?


回答1:


The problem is here:

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

With that order, the response compression middleware will be placed in front of your middleware. That means compression middleware will get request before your middleware, but process response after your middleware.

Try to add your middleware before UseResponseCompression() and you should see compressed content.

Reference: ASP.NET Core Middleware Pipeline



来源:https://stackoverflow.com/questions/50924252/get-the-compressed-response-length-in-asp-net-core

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!