如何在ASP.net 6 Core中设置Content-Length Header以防止传输编码:chunked



我正试图使用.net 6 CORE创建一个中间件,但我不知道如何设置内容长度标头以防止请求返回"传输编码:分块的";头球中间件捕获请求,然后修改请求,然后使用HttpClient将其发送到另一个端点。然后对HttpClient的响应进行操作,并使用操作后的响应更新上下文响应。我似乎无法从中间件获得具有内容长度标头的最终响应,因此它将Transfer Encoding标头设置为chunked。

所需标题

HTTP/1.1 302已找到
缓存控制:私有
内容类型:text/html;charset=utf-8
位置:/geocoder/rest/services
服务器:Microsoft IIS/10.0
X-AspNet-Version:4.0.30119
X-Powered-By:ASP。NET
日期:2022年4月7日星期四14:49:37 GMT
内容长度:138

当前标头

HTTP/1.1 302已找到
内容类型:text/html;charset=utf-8
日期:2022年4月7日星期四14:49:37 GMT
服务器:Microsoft IIS/10.0
缓存控制:私有
位置:/geocoder/rest/services
传输编码:分块
X-AspNet-Version:4.030319
X-Powered-Bby:ASP。NET

ProcessResponseContent是我处理HttpClient响应并将其写回当前上下文响应的地方。

public class ReverseProxyMiddleware2
{
private static readonly HttpClient _httpClient = new HttpClient(new HttpClientHandler { AllowAutoRedirect = false });
private readonly RequestDelegate _nextMiddleware;
private readonly InterceptionConfig _configurationSettings;
private static readonly HashSet<string> _headersToSkipGoingDownstream = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
"Transfer-Encoding",
};
public ReverseProxyMiddleware2(RequestDelegate nextMiddleware, IOptions<InterceptionConfig> optionsSettings)
{
_nextMiddleware = nextMiddleware;
_configurationSettings = optionsSettings.Value;
}
public async Task Invoke(HttpContext context)
{
var targetUri = BuildTargetUri(context.Request);
if (targetUri != null)
{
//Build URL to redirect request to another enpoint
var targetRequestMessage = CreateTargetMessage(context, targetUri);
//Use HttpClient to send request to another endpoint
using (var responseMessage = await _httpClient.SendAsync(targetRequestMessage, HttpCompletionOption.ResponseHeadersRead, context.RequestAborted))
{
context.Response.StatusCode = (int)responseMessage.StatusCode;
//Copy over headers from the HttpClient response to the current context response
CopyFromTargetResponseHeaders(context, responseMessage);
//Update response content is where the problem starts
await ProcessResponseContent(context, responseMessage);
}
return;
}
await _nextMiddleware(context);
}
private async Task ProcessResponseContent(HttpContext context, HttpResponseMessage responseMessage)
{
var content = await responseMessage.Content.ReadAsByteArrayAsync();
if (IsContentOfType(responseMessage, "text/html") ||
IsContentOfType(responseMessage, "text/javascript"))
{
context.Response.Headers.Remove("Content-Length");
//Update content
var newContent = Encoding.UTF8.GetString(content).ReplaceAdaptorUrls(context, _configurationSettings, NetworkFlowEnum.ArcGISToProxyToClient);
//******trying to set content-length header to avoid Transfer-Encoding: chunked
UTF8Encoding encoding = new UTF8Encoding();
byte[] bytes = encoding.GetBytes(newContent);
context.Response.Headers["Content-Length"] = bytes.Length.ToString();
context.Response.OnStarting(() =>
{
//this didn't seem to work either
context.Response.Headers.ContentLength = bytes.Length;
return Task.CompletedTask;
});
//******end trying to set content-length header to avoid Transfer-Encoding: chunked
await context.Response.WriteAsync(newContent, Encoding.UTF8);
}
else
{
await context.Response.Body.WriteAsync(content);
}
}
private bool IsContentOfType(HttpResponseMessage responseMessage, string type)
{
var result = false;
if (responseMessage.Content?.Headers?.ContentType != null)
{
result = responseMessage.Content.Headers.ContentType.MediaType == type;
}
return result;
}
private HttpRequestMessage CreateTargetMessage(HttpContext context, Uri targetUri)
{
var requestMessage = new HttpRequestMessage();
CopyFromOriginalRequestContentAndHeaders(context, requestMessage);
requestMessage.RequestUri = targetUri;
requestMessage.Headers.Host = targetUri.Host;
requestMessage.Method = GetMethod(context.Request.Method);
return requestMessage;
}
private void CopyFromOriginalRequestContentAndHeaders(HttpContext context, HttpRequestMessage requestMessage)
{
var requestMethod = context.Request.Method;
if (!HttpMethods.IsGet(requestMethod) &&
!HttpMethods.IsHead(requestMethod) &&
!HttpMethods.IsDelete(requestMethod) &&
!HttpMethods.IsTrace(requestMethod))
{
var streamContent = new StreamContent(context.Request.Body);
requestMessage.Content = streamContent;
}
foreach (var header in context.Request.Headers)
{
var newValues = header.Value.ToArray().Select(value => value.ReplaceAdaptorUrls(context, _configurationSettings, NetworkFlowEnum.ClientToProxyToArcGIS)).ToArray();
requestMessage.Headers.Add(header.Key, newValues);
}
}
private void CopyFromTargetResponseHeaders(HttpContext context, HttpResponseMessage responseMessage)
{
foreach (var header in responseMessage.Headers)
{
if (_headersToSkipGoingDownstream.Contains(header.Key))
{
continue;
}
var newValues = header.Value.ToArray().Select(value => value.ReplaceAdaptorUrls(context, _configurationSettings, NetworkFlowEnum.ArcGISToProxyToClient)).ToArray();
context.Response.Headers.Add(header.Key, newValues);
}
foreach (var header in responseMessage.Content.Headers)
{
if (_headersToSkipGoingDownstream.Contains(header.Key))
{
continue;
}
var newValues = header.Value.ToArray().Select(value => value.ReplaceAdaptorUrls(context, _configurationSettings, NetworkFlowEnum.ArcGISToProxyToClient)).ToArray();
context.Response.Headers.Add(header.Key, newValues);
}
}
private static HttpMethod GetMethod(string method)
{
if (HttpMethods.IsDelete(method)) return HttpMethod.Delete;
if (HttpMethods.IsGet(method)) return HttpMethod.Get;
if (HttpMethods.IsHead(method)) return HttpMethod.Head;
if (HttpMethods.IsOptions(method)) return HttpMethod.Options;
if (HttpMethods.IsPost(method)) return HttpMethod.Post;
if (HttpMethods.IsPut(method)) return HttpMethod.Put;
if (HttpMethods.IsTrace(method)) return HttpMethod.Trace;
return new HttpMethod(method);
}
private Uri BuildTargetUri(HttpRequest request)
{
string targetUri = null;
//this is more hacking for portal login, seems the url it completely different
if (request.Path.Value.EndsWith("login/config/dojo.js"))
targetUri = $"{_configurationSettings.GISServer.HostURL}/{_configurationSettings.GISServer.ServerAdaptor?.AdaptorName}/login/login/config/dojo.js";
else if (request.Path.Value.EndsWith("jsapi/dojo/dojo.js"))
targetUri = $"{_configurationSettings.GISServer.HostURL}/{_configurationSettings.GISServer.ServerAdaptor?.AdaptorName}/login/jsapi/dojo/dojo.js";
else if (request.Path.Value.EndsWith("login/main.js"))
targetUri = $"{_configurationSettings.GISServer.HostURL}/{_configurationSettings.GISServer.ServerAdaptor?.AdaptorName}/login/login/main.js";
else if (request.Path.Value.EndsWith("login/nls/main_en-us.js"))
targetUri = $"{_configurationSettings.GISServer.HostURL}/{_configurationSettings.GISServer.ServerAdaptor?.AdaptorName}/login/login/nls/main_en-us.js";
else if (request.Path.StartsWithSegments("/geocoder", out var remainingPath))
{
targetUri = $"{_configurationSettings.GISServer.HostURL}/{_configurationSettings.GISServer.ServerAdaptor?.AdaptorName}{remainingPath}";
}
else if (request.Path.StartsWithSegments($"/{_configurationSettings.GISServer.PortalAdaptor?.ProxyAdaptorName}", out var remainingPortalPath))
{
targetUri = $"{_configurationSettings.GISServer.HostURL}/{_configurationSettings.GISServer.PortalAdaptor?.AdaptorName}{remainingPortalPath}";
}
else if (request.Path.StartsWithSegments("/rest", out var remainingRestPath))
{
targetUri = $"{_configurationSettings.GISServer.HostURL}/{_configurationSettings.GISServer.ServerAdaptor?.AdaptorName}/rest{remainingRestPath}";
}
if (targetUri != null && request.QueryString.HasValue)
{
return new Uri(QueryHelpers.AddQueryString(targetUri, request.Query.ReplaceAdaptorUrls(request.HttpContext, _configurationSettings, NetworkFlowEnum.ClientToProxyToArcGIS)));
}
else if (targetUri != null)
return new Uri(targetUri);
return null;
}
}

我认为Visual Studio 2022正在进行的浏览器链接导致了问题。一旦我禁用了它,我就可以按预期设置内容长度。

禁用Visual Studio 2022 中的浏览器链接

最新更新