// ------------------------------------------------------------------------
// 版权信息
// 版权归百小僧及百签科技(广东)有限公司所有。
// 所有权利保留。
// 官方网站: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);
}
}