ContentLength 在生产中为空,但在开发 dotnet 8 c# 中填充

问题描述 投票:0回答:1

(使用 c# dotnet 8)创建了一个非常简单的中间件来跟踪用户、他们连接的端点、请求的大小和响应的大小。 当我运行

dotnet run
时,这在我的机器上完美运行,但是当将其上传到使用 IIS 运行 Windows 2022 的服务器时,响应长度始终为 NULL。

这是中间件

    /// <summary>
    /// Configure our httpRequest to be buffered so we can read it multiple times ourselves
    /// </summary>
    public class UserTrackingMiddleware
    {
        private readonly RequestDelegate _next;

        public UserTrackingMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        public async Task Invoke(HttpContext context)
        {
            //  get host from the request and check if it's in the enumeration of allowed hosts
            var url = context.Request.GetDisplayUrl();
            var querystring = context.Request.QueryString.ToString();
            var endpoint = context.GetEndpoint();

            if (!string.IsNullOrEmpty(querystring))
            {
                url = url.Replace(querystring, string.Empty);
                querystring = querystring.Substring(1);
            }

            if (!string.IsNullOrEmpty(url))
            {
                var scope = context.RequestServices.CreateScope();
                var currentUser = scope.ServiceProvider.GetRequiredService<ICurrentUser>();
                var user = await currentUser.GetAsync();

                if (currentUser.IsAuthenticated)
                {
                    var watch = new Stopwatch();
                    watch.Start();

                    var dataContext = scope.ServiceProvider.GetRequiredService<IDataContext>();

                    // userAgent
                    var userAgent = "unavailable";

                    if (context.Request.Headers.TryGetValue("User-Agent", out var parsedUserAgent))
                        userAgent = parsedUserAgent.ToString();

                    // contentLength
                    long? requestLength = null;

                    if (headerKeys.Contains("Content-Length", StringComparer.OrdinalIgnoreCase))
                        requestLength = long.Parse(context.Request.Headers["Content-Length"].ToString());

                    // headers
                    var httpHeaders = new Dictionary<string, string>();
                    foreach (var header in headerKeys.OrderBy(e => e))
                        httpHeaders.Add(header, context.Request.Headers[header]);

                    var log = new UserTracking
                    {
                        UserId = user.Id,
                        Url = url,
                        QueryString = querystring,
                        RequestLength = requestLength,
                        UserAgent = userAgent,
                        Method = context.Request.Method.ToLower(),
                        Headers = JsonConvert.SerializeObject(httpHeaders)
                    };

                    dataContext.SetAdded(log);

                    try
                    {
                        await dataContext.SaveChangesAsync(context.RequestAborted);

                        // To add Headers AFTER everything you need to do this
                        context.Response.OnStarting(async () =>
                        {
                            watch.Stop();

                            var sqlBuilder = new SqlBuilder("UPDATE UserTracking SET ElapsedMilliseconds = {0}, ResponseLength = {1} WHERE Id = {2}", watch.ElapsedMilliseconds, context.Response.ContentLength, log.Id);
                            await dataContext.ExecuteSqlCommandAsync(sqlBuilder);
                        });
                    }
                    catch (Exception)
                    {
                        // an error occurred here
                    }
                }
            }

            await _next(context);
        }
    }
}

在我的本地机器上,SQL 结果如下所示 local result set

但是在服务器上,可以看到responseLength为NULL server result set

我本地的lauchSettings看起来像这样

    "https": {
      "commandName": "Project",
      "dotnetRunMessages": true,
      "launchBrowser": true,
      "launchUrl": "swagger",
      "applicationUrl": "https://localhost:5001;http://localhost:5000",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    }

我读到响应 contentLength 为 NULL,因为响应没有缓冲,你需要将其放入内存流中才能获取响应长度,但为什么它在开发中有效但在服务器上无效?

中间件是通过 Program.cs 添加的,如下所示


// must be done before all other middleware, expect developer exception page
app.UseHsts(options =>
{
    options.MaxAge(120, 0, 1, 0);
    options.AllResponses();
    options.IncludeSubdomains();
});

// middleware
app.UseGlobalExceptionHandler();
app.UseOperationCancelledExceptionHandler();
app.UseResponseCaching();
app.UseResponseCompression();

// Content Security Policy (CSP)
app.UseSecurityPolicy();
app.UseCsp(options =>
{
    ...
});

// swagger
app.UseStaticFiles(new StaticFileOptions()
{
    ...
});

// app
app.UseStaticFiles(new StaticFileOptions()
{
    FileProvider = new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), @"wwwroot", "app")),

    // accessing wwwroot inside folder contents
    RequestPath = new PathString(""),

    // add cache headers
    OnPrepareResponse = (context) =>
    {
        var headers = context.Context.Response.GetTypedHeaders();
        headers.CacheControl = new CacheControlHeaderValue()
        {
            Public = false,
            Private = true,
            MaxAge = TimeSpan.FromHours(24)
        };

        // these are also in the securityPolicyMiddleware
        headers.Set("Permissions-Policy", "accelerometer=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), usb=()");
        headers.Set("Referrer-Policy", "strict-origin-when-cross-origin");
        headers.Set("X-Content-Type-Options", "nosniff");
        headers.Set("X-Frame-Options", "DENY");
        headers.Set("X-Permitted-Cross-Domain-Policies", "none");
    }
});

// Enable middleware to serve generated Swagger as a JSON endpoint
app.UseSwagger(options =>
{
    ...
});

// Enable middleware to serve swagger-ui assets (HTML, JS, CSS etc.)
app.UseSwaggerUI(options =>
{
    ...
});

// routing
app.UseRouting();

// CORS - Configuring is with MvcExtensions
app.UseCors();

// comment this out and you get an error saying 
// InvalidOperationException: No authentication handler is configured to handle the scheme: Microsoft.AspNet.Identity.External
app.UseAuthentication();

// for authorization headers
app.UseAuthorization();

// antiforgery
app.UseAntiforgery();

// user tracking  <!--  HERE
app.UseUserTracking();
c# asp.net .net asp.net-mvc .net-core
1个回答
0
投票

根据您提供给我们的信息,您使用带有 IIS 的 Windows 2022。此版本的 IIS 适用于 HTTP/2 (https://learn.microsoft.com/en-us/iis/get-started/whats-new-in-iis-10/http2-on-iis)。

不发送 Content-Length 标头,因为 HTTP/2 和 HTTP/3 框架基于

END_STREAM
标志。

HTTP/2/3 是带有帧的二进制协议,可以对其进行编码 该请求/响应的最后一帧中的信息。它是一个 END_STREAM 标志设置为 true 的标头或数据帧 判断是否有请求/响应内容,如果有,是什么帧 携带最后的内容字节。

总而言之,Content-Length 标头在 HTTP/2 中不是必需的, HTTP/3 因为它们具有与 HTTP/1.1 不同的框架。

这是解释为什么未设置的原始答案:Why is content-length header not sent over HTTP/3?

© www.soinside.com 2019 - 2024. All rights reserved.