获取 ASP.net 核心中的压缩响应长度
Get the compressed response length in ASP.net Core
我在 Kestrel 上使用 .net Core 运行 构建了一个 RESTful API。我刚刚使用 "GzipCompressionProvider" 中间件启用了压缩,如 here.
所述
我还使用自定义记录器,将所有 requests/responses 记录到数据库,响应和请求长度如下:
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 始终是预压缩长度。关于我需要更改的任何建议。我假设问题是响应仅在刷新到客户端时才被压缩?在那之后我可以添加我的记录器吗?
问题出在这里:
app.UseResponseCompression();
app.UseMiddleware<myMiddleware.LoggerMiddleware>();
按照该顺序,响应压缩中间件将被放置在您的中间件前面。这意味着压缩中间件将在您的中间件之前收到请求,但在您的中间件之后处理响应。
尝试在 UseResponseCompression()
之前添加您的中间件,您应该会看到压缩内容。
我在 Kestrel 上使用 .net Core 运行 构建了一个 RESTful API。我刚刚使用 "GzipCompressionProvider" 中间件启用了压缩,如 here.
所述我还使用自定义记录器,将所有 requests/responses 记录到数据库,响应和请求长度如下:
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 始终是预压缩长度。关于我需要更改的任何建议。我假设问题是响应仅在刷新到客户端时才被压缩?在那之后我可以添加我的记录器吗?
问题出在这里:
app.UseResponseCompression();
app.UseMiddleware<myMiddleware.LoggerMiddleware>();
按照该顺序,响应压缩中间件将被放置在您的中间件前面。这意味着压缩中间件将在您的中间件之前收到请求,但在您的中间件之后处理响应。
尝试在 UseResponseCompression()
之前添加您的中间件,您应该会看到压缩内容。