// ------------------------------------------------------------------------ // 版权信息 // 版权归百小僧及百签科技(广东)有限公司所有。 // 所有权利保留。 // 官方网站:https://baiqian.com // // 许可证信息 // 项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。 // 许可证的完整文本可以在源代码树根目录中的 LICENSE-APACHE 和 LICENSE-MIT 文件中找到。 // ------------------------------------------------------------------------ using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using System.Net.Http.Headers; using ThingsGateway.Extension; namespace ThingsGateway.HttpRemote.Extensions; /// /// 拓展类 /// public static partial class HttpContextExtensions { /// /// 忽略在转发时需要跳过的响应标头列表 /// /// /// /// /// Content-Type: /// /// 非标准的 Content-Type 值(例如 text/plain; charset=utf-8 /// )可能会导致“No output formatter was found for content types 'text/plain; charset=utf-8, text/plain; /// charset=utf-8' to write the response.”错误。忽略此标头以防止此类错误。 /// /// /// /// Transfer-Encoding: /// 当响应标头包含 Transfer-Encoding: chunked 时,可能导致响应处理过程无限期挂起。忽略此标头可避免该问题。 /// /// /// internal static readonly HashSet _ignoreResponseHeaders = [ "Content-Type", "Connection", "Transfer-Encoding", "Keep-Alive", "Upgrade", "Proxy-Connection" ]; /// /// 转发 到新的 HTTP 远程地址 /// /// /// /// /// 转发地址。若为空则尝试从请求标头 X-Forward-To 中获取目标地址。 /// 自定义配置委托 /// /// /// /// /// /// /// /// /// public static HttpResponseMessage? Forward(this HttpContext? httpContext, string? requestUri = null, Action? configure = null, HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead, HttpContextForwardOptions? forwardOptions = null) => Forward(httpContext, Helpers.ParseHttpMethod(httpContext?.Request.Method), string.IsNullOrWhiteSpace(requestUri) ? null : new Uri(requestUri, UriKind.RelativeOrAbsolute), configure, completionOption, forwardOptions); /// /// 转发 到新的 HTTP 远程地址 /// /// /// /// /// 转发方式 /// 转发地址。若为空则尝试从请求标头 X-Forward-To 中获取目标地址。 /// 自定义配置委托 /// /// /// /// /// /// /// /// /// public static HttpResponseMessage? Forward(this HttpContext? httpContext, HttpMethod httpMethod, string? requestUri = null, Action? configure = null, HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead, HttpContextForwardOptions? forwardOptions = null) => Forward(httpContext, httpMethod, string.IsNullOrWhiteSpace(requestUri) ? null : new Uri(requestUri, UriKind.RelativeOrAbsolute), configure, completionOption, forwardOptions); /// /// 转发 到新的 HTTP 远程地址 /// /// /// /// /// 转发地址。若为空则尝试从请求标头 X-Forward-To 中获取目标地址。 /// 自定义配置委托 /// /// /// /// /// /// /// /// /// public static HttpResponseMessage? Forward(this HttpContext? httpContext, Uri? requestUri = null, Action? configure = null, HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead, HttpContextForwardOptions? forwardOptions = null) => Forward(httpContext, Helpers.ParseHttpMethod(httpContext?.Request.Method), requestUri, configure, completionOption, forwardOptions); /// /// 转发 到新的 HTTP 远程地址 /// /// /// /// /// 转发方式 /// 转发地址。若为空则尝试从请求标头 X-Forward-To 中获取目标地址。 /// 自定义配置委托 /// /// /// /// /// /// /// /// /// public static HttpResponseMessage? Forward(this HttpContext? httpContext, HttpMethod httpMethod, Uri? requestUri = null, Action? configure = null, HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead, HttpContextForwardOptions? forwardOptions = null) { // 空检查 ArgumentNullException.ThrowIfNull(httpContext); ArgumentNullException.ThrowIfNull(httpMethod); // 初始化转发所需的服务 var (httpContextForwardBuilder, httpRequestBuilder, httpRemoteService) = PrepareForwardService(httpContext, httpMethod, requestUri, configure, forwardOptions); // 发送 HTTP 远程请求 var httpResponseMessage = httpRemoteService.Send(httpRequestBuilder, completionOption, httpContext.RequestAborted); // 根据配置选项将 HttpResponseMessage 信息转发到 HttpContext 中 ForwardResponseMessage(httpContext, httpResponseMessage, httpContextForwardBuilder.ForwardOptions); return httpResponseMessage; } /// /// 转发 到新的 HTTP 远程地址 /// /// /// /// /// 转发地址。若为空则尝试从请求标头 X-Forward-To 中获取目标地址。 /// 自定义配置委托 /// /// /// /// /// /// /// /// /// public static Task ForwardAsync(this HttpContext? httpContext, string? requestUri = null, Action? configure = null, HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead, HttpContextForwardOptions? forwardOptions = null) => ForwardAsync(httpContext, Helpers.ParseHttpMethod(httpContext?.Request.Method), string.IsNullOrWhiteSpace(requestUri) ? null : new Uri(requestUri, UriKind.RelativeOrAbsolute), configure, completionOption, forwardOptions); /// /// 转发 到新的 HTTP 远程地址 /// /// /// /// /// 转发方式 /// 转发地址。若为空则尝试从请求标头 X-Forward-To 中获取目标地址。 /// 自定义配置委托 /// /// /// /// /// /// /// /// /// public static Task ForwardAsync(this HttpContext? httpContext, HttpMethod httpMethod, string? requestUri = null, Action? configure = null, HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead, HttpContextForwardOptions? forwardOptions = null) => ForwardAsync(httpContext, httpMethod, string.IsNullOrWhiteSpace(requestUri) ? null : new Uri(requestUri, UriKind.RelativeOrAbsolute), configure, completionOption, forwardOptions); /// /// 转发 到新的 HTTP 远程地址 /// /// /// /// /// 转发地址。若为空则尝试从请求标头 X-Forward-To 中获取目标地址。 /// 自定义配置委托 /// /// /// /// /// /// /// /// /// public static Task ForwardAsync(this HttpContext? httpContext, Uri? requestUri = null, Action? configure = null, HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead, HttpContextForwardOptions? forwardOptions = null) => ForwardAsync(httpContext, Helpers.ParseHttpMethod(httpContext?.Request.Method), requestUri, configure, completionOption, forwardOptions); /// /// 转发 到新的 HTTP 远程地址 /// /// /// /// /// 转发方式 /// 转发地址。若为空则尝试从请求标头 X-Forward-To 中获取目标地址。 /// 自定义配置委托 /// /// /// /// /// /// /// /// /// public static async Task ForwardAsync(this HttpContext? httpContext, HttpMethod httpMethod, Uri? requestUri = null, Action? configure = null, HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead, HttpContextForwardOptions? forwardOptions = null) { // 空检查 ArgumentNullException.ThrowIfNull(httpContext); ArgumentNullException.ThrowIfNull(httpMethod); // 初始化转发所需的服务 var (httpContextForwardBuilder, httpRequestBuilder, httpRemoteService) = await PrepareForwardServiceAsync(httpContext, httpMethod, requestUri, configure, forwardOptions).ConfigureAwait(false); // 发送 HTTP 远程请求 var httpResponseMessage = await httpRemoteService.SendAsync(httpRequestBuilder, completionOption, httpContext.RequestAborted).ConfigureAwait(false); // 根据配置选项将 HttpResponseMessage 信息转发到 HttpContext 中 ForwardResponseMessage(httpContext, httpResponseMessage, httpContextForwardBuilder.ForwardOptions); return httpResponseMessage; } /// /// 转发 到新的 HTTP 远程地址 /// /// /// /// /// 转发地址。若为空则尝试从请求标头 X-Forward-To 中获取目标地址。 /// 自定义配置委托 /// /// /// /// /// /// /// 转换的目标类型 /// /// /// public static HttpRemoteResult? Forward(this HttpContext? httpContext, string? requestUri = null, Action? configure = null, HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead, HttpContextForwardOptions? forwardOptions = null) => Forward(httpContext, Helpers.ParseHttpMethod(httpContext?.Request.Method), string.IsNullOrWhiteSpace(requestUri) ? null : new Uri(requestUri, UriKind.RelativeOrAbsolute), configure, completionOption, forwardOptions); /// /// 转发 到新的 HTTP 远程地址 /// /// /// /// /// 转发方式 /// 转发地址。若为空则尝试从请求标头 X-Forward-To 中获取目标地址。 /// 自定义配置委托 /// /// /// /// /// /// /// 转换的目标类型 /// /// /// public static HttpRemoteResult? Forward(this HttpContext? httpContext, HttpMethod httpMethod, string? requestUri = null, Action? configure = null, HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead, HttpContextForwardOptions? forwardOptions = null) => Forward(httpContext, httpMethod, string.IsNullOrWhiteSpace(requestUri) ? null : new Uri(requestUri, UriKind.RelativeOrAbsolute), configure, completionOption, forwardOptions); /// /// 转发 到新的 HTTP 远程地址 /// /// /// /// /// 转发地址。若为空则尝试从请求标头 X-Forward-To 中获取目标地址。 /// 自定义配置委托 /// /// /// /// /// /// /// 转换的目标类型 /// /// /// public static HttpRemoteResult? Forward(this HttpContext? httpContext, Uri? requestUri = null, Action? configure = null, HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead, HttpContextForwardOptions? forwardOptions = null) => Forward(httpContext, Helpers.ParseHttpMethod(httpContext?.Request.Method), requestUri, configure, completionOption, forwardOptions); /// /// 转发 到新的 HTTP 远程地址 /// /// /// /// /// 转发方式 /// 转发地址。若为空则尝试从请求标头 X-Forward-To 中获取目标地址。 /// 自定义配置委托 /// /// /// /// /// /// /// 转换的目标类型 /// /// /// public static HttpRemoteResult? Forward(this HttpContext? httpContext, HttpMethod httpMethod, Uri? requestUri = null, Action? configure = null, HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead, HttpContextForwardOptions? forwardOptions = null) { // 空检查 ArgumentNullException.ThrowIfNull(httpContext); ArgumentNullException.ThrowIfNull(httpMethod); // 初始化转发所需的服务 var (httpContextForwardBuilder, httpRequestBuilder, httpRemoteService) = PrepareForwardService(httpContext, httpMethod, requestUri, configure, forwardOptions); // 发送 HTTP 远程请求 var result = httpRemoteService.Send(httpRequestBuilder, completionOption, httpContext.RequestAborted); // 根据配置选项将 HttpResponseMessage 信息转发到 HttpContext 中 ForwardResponseMessage(httpContext, result?.ResponseMessage, httpContextForwardBuilder.ForwardOptions); return result; } /// /// 转发 到新的 HTTP 远程地址 /// /// /// /// /// 转发地址。若为空则尝试从请求标头 X-Forward-To 中获取目标地址。 /// 自定义配置委托 /// /// /// /// /// /// /// 转换的目标类型 /// /// /// public static Task?> ForwardAsync(this HttpContext? httpContext, string? requestUri = null, Action? configure = null, HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead, HttpContextForwardOptions? forwardOptions = null) => ForwardAsync(httpContext, Helpers.ParseHttpMethod(httpContext?.Request.Method), string.IsNullOrWhiteSpace(requestUri) ? null : new Uri(requestUri, UriKind.RelativeOrAbsolute), configure, completionOption, forwardOptions); /// /// 转发 到新的 HTTP 远程地址 /// /// /// /// /// 转发方式 /// 转发地址。若为空则尝试从请求标头 X-Forward-To 中获取目标地址。 /// 自定义配置委托 /// /// /// /// /// /// /// 转换的目标类型 /// /// /// public static Task?> ForwardAsync(this HttpContext? httpContext, HttpMethod httpMethod, string? requestUri = null, Action? configure = null, HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead, HttpContextForwardOptions? forwardOptions = null) => ForwardAsync(httpContext, httpMethod, string.IsNullOrWhiteSpace(requestUri) ? null : new Uri(requestUri, UriKind.RelativeOrAbsolute), configure, completionOption, forwardOptions); /// /// 转发 到新的 HTTP 远程地址 /// /// /// /// /// 转发地址。若为空则尝试从请求标头 X-Forward-To 中获取目标地址。 /// 自定义配置委托 /// /// /// /// /// /// /// 转换的目标类型 /// /// /// public static Task?> ForwardAsync(this HttpContext? httpContext, Uri? requestUri = null, Action? configure = null, HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead, HttpContextForwardOptions? forwardOptions = null) => ForwardAsync(httpContext, Helpers.ParseHttpMethod(httpContext?.Request.Method), requestUri, configure, completionOption, forwardOptions); /// /// 转发 到新的 HTTP 远程地址 /// /// /// /// /// 转发方式 /// 转发地址。若为空则尝试从请求标头 X-Forward-To 中获取目标地址。 /// 自定义配置委托 /// /// /// /// /// /// /// 转换的目标类型 /// /// /// public static async Task?> ForwardAsync(this HttpContext? httpContext, HttpMethod httpMethod, Uri? requestUri = null, Action? configure = null, HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead, HttpContextForwardOptions? forwardOptions = null) { // 空检查 ArgumentNullException.ThrowIfNull(httpContext); ArgumentNullException.ThrowIfNull(httpMethod); // 初始化转发所需的服务 var (httpContextForwardBuilder, httpRequestBuilder, httpRemoteService) = await PrepareForwardServiceAsync(httpContext, httpMethod, requestUri, configure, forwardOptions).ConfigureAwait(false); // 发送 HTTP 远程请求 var result = await httpRemoteService.SendAsync(httpRequestBuilder, completionOption, httpContext.RequestAborted).ConfigureAwait(false); // 根据配置选项将 HttpResponseMessage 信息转发到 HttpContext 中 ForwardResponseMessage(httpContext, result?.ResponseMessage, httpContextForwardBuilder.ForwardOptions); return result; } /// /// 创建 实例 /// /// /// /// /// 转发方式 /// 转发地址。若为空则尝试从请求标头 X-Forward-To 中获取目标地址。 /// /// /// /// /// /// public static HttpContextForwardBuilder CreateForwardBuilder(this HttpContext? httpContext, HttpMethod httpMethod, string? requestUri = null, HttpContextForwardOptions? forwardOptions = null) => CreateForwardBuilder(httpContext, httpMethod, string.IsNullOrWhiteSpace(requestUri) ? null : new Uri(requestUri, UriKind.RelativeOrAbsolute), forwardOptions); /// /// 创建 实例 /// /// /// /// /// 转发地址。若为空则尝试从请求标头 X-Forward-To 中获取目标地址。 /// /// /// /// /// /// public static HttpContextForwardBuilder CreateForwardBuilder(this HttpContext? httpContext, string? requestUri = null, HttpContextForwardOptions? forwardOptions = null) => CreateForwardBuilder(httpContext, Helpers.ParseHttpMethod(httpContext?.Request.Method), string.IsNullOrWhiteSpace(requestUri) ? null : new Uri(requestUri, UriKind.RelativeOrAbsolute), forwardOptions); /// /// 创建 实例 /// /// /// /// /// 转发方式 /// 转发地址。若为空则尝试从请求标头 X-Forward-To 中获取目标地址。 /// /// /// /// /// /// public static HttpContextForwardBuilder CreateForwardBuilder(this HttpContext? httpContext, HttpMethod httpMethod, Uri? requestUri = null, HttpContextForwardOptions? forwardOptions = null) => new(httpContext, httpMethod, requestUri, forwardOptions); /// /// 创建 实例 /// /// /// /// /// 转发地址。若为空则尝试从请求标头 X-Forward-To 中获取目标地址。 /// /// /// /// /// /// public static HttpContextForwardBuilder CreateForwardBuilder(this HttpContext? httpContext, Uri? requestUri = null, HttpContextForwardOptions? forwardOptions = null) => new(httpContext, Helpers.ParseHttpMethod(httpContext?.Request.Method), requestUri, forwardOptions); /// /// 根据配置选项将 信息转发到 中 /// /// /// /// /// /// /// /// /// /// internal static void ForwardResponseMessage(HttpContext httpContext, HttpResponseMessage? httpResponseMessage, HttpContextForwardOptions forwardOptions) { // 空检查 ArgumentNullException.ThrowIfNull(httpContext); ArgumentNullException.ThrowIfNull(forwardOptions); // 空检查 if (httpResponseMessage is null) { // 输出调试信息 Debugging.Error("The response content was not read, as it was empty."); return; } // 获取 HttpResponse 实例 var httpResponse = httpContext.Response; // 检查是否配置了响应状态码转发 if (forwardOptions.WithResponseStatusCode) { httpResponse.StatusCode = (int)httpResponseMessage.StatusCode; } // 检查是否配置了响应标头转发 if (forwardOptions.WithResponseHeaders) { ForwardHttpHeaders(httpResponse, httpResponseMessage.Headers, forwardOptions); } // 检查是否配置了响应内容标头转发 if (forwardOptions.WithResponseContentHeaders) { ForwardHttpHeaders(httpResponse, httpResponseMessage.Content.Headers, forwardOptions); } // 调用用于在转发响应之前执行自定义操作 forwardOptions.OnForward?.Invoke(httpContext, httpResponseMessage); } /// /// 转发 HTTP 标头 /// /// /// /// /// /// /// /// /// /// internal static void ForwardHttpHeaders(HttpResponse httpResponse, HttpHeaders httpHeaders, HttpContextForwardOptions forwardOptions) { // 初始化忽略在转发时需要跳过的响应标头列表 var ignoreResponseHeaders = _ignoreResponseHeaders.ConcatIgnoreNull(forwardOptions.IgnoreResponseHeaders).Distinct().ToArray(); // 逐条更新响应标头 foreach (var (key, values) in httpHeaders) { // 忽略特定响应标头 if (key.IsIn(ignoreResponseHeaders, StringComparer.OrdinalIgnoreCase)) { continue; } httpResponse.Headers[key] = values.ToArray(); } } /// /// 初始化转发所需的服务 /// /// /// /// /// 转发方式 /// 转发地址。若为空则尝试从请求标头 X-Forward-To 中获取目标地址。 /// 自定义配置委托 /// /// /// /// /// /// internal static (HttpContextForwardBuilder httpContextForwardBuilder, HttpRequestBuilder httpRequestBuilder, IHttpRemoteService httpRemoteService) PrepareForwardService(HttpContext httpContext, HttpMethod httpMethod, Uri? requestUri, Action? configure = null, HttpContextForwardOptions? forwardOptions = null) { // 创建 HttpContextForwardBuilder 实例 var httpContextForwardBuilder = CreateForwardBuilder(httpContext, httpMethod, requestUri, forwardOptions); // 构建 HttpRequestBuilder 实例 var httpRequestBuilder = httpContextForwardBuilder.Build(configure); // 获取 IHttpRemoteService 实例 var httpRemoteService = httpContext.RequestServices.GetRequiredService(); return (httpContextForwardBuilder, httpRequestBuilder, httpRemoteService); } /// /// 初始化转发所需的服务 /// /// /// /// /// 转发方式 /// 转发地址。若为空则尝试从请求标头 X-Forward-To 中获取目标地址。 /// 自定义配置委托 /// /// /// /// /// /// internal static async Task<(HttpContextForwardBuilder httpContextForwardBuilder, HttpRequestBuilder httpRequestBuilder, IHttpRemoteService httpRemoteService)> PrepareForwardServiceAsync(HttpContext httpContext, HttpMethod httpMethod, Uri? requestUri, Action? configure = null, HttpContextForwardOptions? forwardOptions = null) { // 创建 HttpContextForwardBuilder 实例 var httpContextForwardBuilder = CreateForwardBuilder(httpContext, httpMethod, requestUri, forwardOptions); // 构建 HttpRequestBuilder 实例 var httpRequestBuilder = await httpContextForwardBuilder.BuildAsync(configure).ConfigureAwait(false); // 获取 IHttpRemoteService 实例 var httpRemoteService = httpContext.RequestServices.GetRequiredService(); return (httpContextForwardBuilder, httpRequestBuilder, httpRemoteService); } }