746 lines
34 KiB
C#
746 lines
34 KiB
C#
// ------------------------------------------------------------------------
|
||
// 版权信息
|
||
// 版权归百小僧及百签科技(广东)有限公司所有。
|
||
// 所有权利保留。
|
||
// 官方网站: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;
|
||
|
||
/// <summary>
|
||
/// <see cref="HttpContext" /> 拓展类
|
||
/// </summary>
|
||
public static partial class HttpContextExtensions
|
||
{
|
||
/// <summary>
|
||
/// 忽略在转发时需要跳过的响应标头列表
|
||
/// </summary>
|
||
/// <remarks>
|
||
/// <list type="bullet">
|
||
/// <item>
|
||
/// <term>Content-Type: </term>
|
||
/// <description>
|
||
/// 非标准的 <c>Content-Type</c> 值(例如 <c>text/plain; charset=utf-8</c>
|
||
/// )可能会导致“No output formatter was found for content types 'text/plain; charset=utf-8, text/plain;
|
||
/// charset=utf-8' to write the response.”错误。忽略此标头以防止此类错误。
|
||
/// </description>
|
||
/// </item>
|
||
/// <item>
|
||
/// <term>Transfer-Encoding: </term>
|
||
/// <description>当响应标头包含 <c>Transfer-Encoding: chunked</c> 时,可能导致响应处理过程无限期挂起。忽略此标头可避免该问题。</description>
|
||
/// </item>
|
||
/// </list>
|
||
/// </remarks>
|
||
internal static readonly HashSet<string> _ignoreResponseHeaders =
|
||
[
|
||
"Content-Type", "Connection", "Transfer-Encoding", "Keep-Alive", "Upgrade", "Proxy-Connection"
|
||
];
|
||
|
||
/// <summary>
|
||
/// 转发 <see cref="HttpContext" /> 到新的 HTTP 远程地址
|
||
/// </summary>
|
||
/// <param name="httpContext">
|
||
/// <see cref="HttpContext" />
|
||
/// </param>
|
||
/// <param name="requestUri">转发地址。若为空则尝试从请求标头 <c>X-Forward-To</c> 中获取目标地址。</param>
|
||
/// <param name="configure">自定义配置委托</param>
|
||
/// <param name="completionOption">
|
||
/// <see cref="HttpCompletionOption" />
|
||
/// </param>
|
||
/// <param name="forwardOptions">
|
||
/// <see cref="HttpContextForwardOptions" />
|
||
/// </param>
|
||
/// <returns>
|
||
/// <see cref="HttpResponseMessage" />
|
||
/// </returns>
|
||
public static HttpResponseMessage? Forward(this HttpContext? httpContext, string? requestUri = null,
|
||
Action<HttpRequestBuilder>? 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);
|
||
|
||
/// <summary>
|
||
/// 转发 <see cref="HttpContext" /> 到新的 HTTP 远程地址
|
||
/// </summary>
|
||
/// <param name="httpContext">
|
||
/// <see cref="HttpContext" />
|
||
/// </param>
|
||
/// <param name="httpMethod">转发方式</param>
|
||
/// <param name="requestUri">转发地址。若为空则尝试从请求标头 <c>X-Forward-To</c> 中获取目标地址。</param>
|
||
/// <param name="configure">自定义配置委托</param>
|
||
/// <param name="completionOption">
|
||
/// <see cref="HttpCompletionOption" />
|
||
/// </param>
|
||
/// <param name="forwardOptions">
|
||
/// <see cref="HttpContextForwardOptions" />
|
||
/// </param>
|
||
/// <returns>
|
||
/// <see cref="HttpResponseMessage" />
|
||
/// </returns>
|
||
public static HttpResponseMessage? Forward(this HttpContext? httpContext, HttpMethod httpMethod,
|
||
string? requestUri = null, Action<HttpRequestBuilder>? configure = null,
|
||
HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead,
|
||
HttpContextForwardOptions? forwardOptions = null) =>
|
||
Forward(httpContext, httpMethod,
|
||
string.IsNullOrWhiteSpace(requestUri) ? null : new Uri(requestUri, UriKind.RelativeOrAbsolute), configure,
|
||
completionOption, forwardOptions);
|
||
|
||
/// <summary>
|
||
/// 转发 <see cref="HttpContext" /> 到新的 HTTP 远程地址
|
||
/// </summary>
|
||
/// <param name="httpContext">
|
||
/// <see cref="HttpContext" />
|
||
/// </param>
|
||
/// <param name="requestUri">转发地址。若为空则尝试从请求标头 <c>X-Forward-To</c> 中获取目标地址。</param>
|
||
/// <param name="configure">自定义配置委托</param>
|
||
/// <param name="completionOption">
|
||
/// <see cref="HttpCompletionOption" />
|
||
/// </param>
|
||
/// <param name="forwardOptions">
|
||
/// <see cref="HttpContextForwardOptions" />
|
||
/// </param>
|
||
/// <returns>
|
||
/// <see cref="HttpResponseMessage" />
|
||
/// </returns>
|
||
public static HttpResponseMessage? Forward(this HttpContext? httpContext, Uri? requestUri = null,
|
||
Action<HttpRequestBuilder>? configure = null,
|
||
HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead,
|
||
HttpContextForwardOptions? forwardOptions = null) =>
|
||
Forward(httpContext, Helpers.ParseHttpMethod(httpContext?.Request.Method), requestUri,
|
||
configure, completionOption, forwardOptions);
|
||
|
||
/// <summary>
|
||
/// 转发 <see cref="HttpContext" /> 到新的 HTTP 远程地址
|
||
/// </summary>
|
||
/// <param name="httpContext">
|
||
/// <see cref="HttpContext" />
|
||
/// </param>
|
||
/// <param name="httpMethod">转发方式</param>
|
||
/// <param name="requestUri">转发地址。若为空则尝试从请求标头 <c>X-Forward-To</c> 中获取目标地址。</param>
|
||
/// <param name="configure">自定义配置委托</param>
|
||
/// <param name="completionOption">
|
||
/// <see cref="HttpCompletionOption" />
|
||
/// </param>
|
||
/// <param name="forwardOptions">
|
||
/// <see cref="HttpContextForwardOptions" />
|
||
/// </param>
|
||
/// <returns>
|
||
/// <see cref="HttpResponseMessage" />
|
||
/// </returns>
|
||
public static HttpResponseMessage? Forward(this HttpContext? httpContext, HttpMethod httpMethod,
|
||
Uri? requestUri = null, Action<HttpRequestBuilder>? 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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 转发 <see cref="HttpContext" /> 到新的 HTTP 远程地址
|
||
/// </summary>
|
||
/// <param name="httpContext">
|
||
/// <see cref="HttpContext" />
|
||
/// </param>
|
||
/// <param name="requestUri">转发地址。若为空则尝试从请求标头 <c>X-Forward-To</c> 中获取目标地址。</param>
|
||
/// <param name="configure">自定义配置委托</param>
|
||
/// <param name="completionOption">
|
||
/// <see cref="HttpCompletionOption" />
|
||
/// </param>
|
||
/// <param name="forwardOptions">
|
||
/// <see cref="HttpContextForwardOptions" />
|
||
/// </param>
|
||
/// <returns>
|
||
/// <see cref="HttpResponseMessage" />
|
||
/// </returns>
|
||
public static Task<HttpResponseMessage?> ForwardAsync(this HttpContext? httpContext, string? requestUri = null,
|
||
Action<HttpRequestBuilder>? 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);
|
||
|
||
/// <summary>
|
||
/// 转发 <see cref="HttpContext" /> 到新的 HTTP 远程地址
|
||
/// </summary>
|
||
/// <param name="httpContext">
|
||
/// <see cref="HttpContext" />
|
||
/// </param>
|
||
/// <param name="httpMethod">转发方式</param>
|
||
/// <param name="requestUri">转发地址。若为空则尝试从请求标头 <c>X-Forward-To</c> 中获取目标地址。</param>
|
||
/// <param name="configure">自定义配置委托</param>
|
||
/// <param name="completionOption">
|
||
/// <see cref="HttpCompletionOption" />
|
||
/// </param>
|
||
/// <param name="forwardOptions">
|
||
/// <see cref="HttpContextForwardOptions" />
|
||
/// </param>
|
||
/// <returns>
|
||
/// <see cref="HttpResponseMessage" />
|
||
/// </returns>
|
||
public static Task<HttpResponseMessage?> ForwardAsync(this HttpContext? httpContext, HttpMethod httpMethod,
|
||
string? requestUri = null, Action<HttpRequestBuilder>? configure = null,
|
||
HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead,
|
||
HttpContextForwardOptions? forwardOptions = null) =>
|
||
ForwardAsync(httpContext, httpMethod,
|
||
string.IsNullOrWhiteSpace(requestUri) ? null : new Uri(requestUri, UriKind.RelativeOrAbsolute), configure,
|
||
completionOption, forwardOptions);
|
||
|
||
/// <summary>
|
||
/// 转发 <see cref="HttpContext" /> 到新的 HTTP 远程地址
|
||
/// </summary>
|
||
/// <param name="httpContext">
|
||
/// <see cref="HttpContext" />
|
||
/// </param>
|
||
/// <param name="requestUri">转发地址。若为空则尝试从请求标头 <c>X-Forward-To</c> 中获取目标地址。</param>
|
||
/// <param name="configure">自定义配置委托</param>
|
||
/// <param name="completionOption">
|
||
/// <see cref="HttpCompletionOption" />
|
||
/// </param>
|
||
/// <param name="forwardOptions">
|
||
/// <see cref="HttpContextForwardOptions" />
|
||
/// </param>
|
||
/// <returns>
|
||
/// <see cref="HttpResponseMessage" />
|
||
/// </returns>
|
||
public static Task<HttpResponseMessage?> ForwardAsync(this HttpContext? httpContext, Uri? requestUri = null,
|
||
Action<HttpRequestBuilder>? configure = null,
|
||
HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead,
|
||
HttpContextForwardOptions? forwardOptions = null) =>
|
||
ForwardAsync(httpContext, Helpers.ParseHttpMethod(httpContext?.Request.Method), requestUri,
|
||
configure, completionOption, forwardOptions);
|
||
|
||
/// <summary>
|
||
/// 转发 <see cref="HttpContext" /> 到新的 HTTP 远程地址
|
||
/// </summary>
|
||
/// <param name="httpContext">
|
||
/// <see cref="HttpContext" />
|
||
/// </param>
|
||
/// <param name="httpMethod">转发方式</param>
|
||
/// <param name="requestUri">转发地址。若为空则尝试从请求标头 <c>X-Forward-To</c> 中获取目标地址。</param>
|
||
/// <param name="configure">自定义配置委托</param>
|
||
/// <param name="completionOption">
|
||
/// <see cref="HttpCompletionOption" />
|
||
/// </param>
|
||
/// <param name="forwardOptions">
|
||
/// <see cref="HttpContextForwardOptions" />
|
||
/// </param>
|
||
/// <returns>
|
||
/// <see cref="HttpResponseMessage" />
|
||
/// </returns>
|
||
public static async Task<HttpResponseMessage?> ForwardAsync(this HttpContext? httpContext, HttpMethod httpMethod,
|
||
Uri? requestUri = null, Action<HttpRequestBuilder>? 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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 转发 <see cref="HttpContext" /> 到新的 HTTP 远程地址
|
||
/// </summary>
|
||
/// <param name="httpContext">
|
||
/// <see cref="HttpContext" />
|
||
/// </param>
|
||
/// <param name="requestUri">转发地址。若为空则尝试从请求标头 <c>X-Forward-To</c> 中获取目标地址。</param>
|
||
/// <param name="configure">自定义配置委托</param>
|
||
/// <param name="completionOption">
|
||
/// <see cref="HttpCompletionOption" />
|
||
/// </param>
|
||
/// <param name="forwardOptions">
|
||
/// <see cref="HttpContextForwardOptions" />
|
||
/// </param>
|
||
/// <typeparam name="TResult">转换的目标类型</typeparam>
|
||
/// <returns>
|
||
/// <see cref="HttpRemoteResult{TResult}" />
|
||
/// </returns>
|
||
public static HttpRemoteResult<TResult>? Forward<TResult>(this HttpContext? httpContext, string? requestUri = null,
|
||
Action<HttpRequestBuilder>? configure = null,
|
||
HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead,
|
||
HttpContextForwardOptions? forwardOptions = null) =>
|
||
Forward<TResult>(httpContext, Helpers.ParseHttpMethod(httpContext?.Request.Method),
|
||
string.IsNullOrWhiteSpace(requestUri) ? null : new Uri(requestUri, UriKind.RelativeOrAbsolute), configure,
|
||
completionOption, forwardOptions);
|
||
|
||
/// <summary>
|
||
/// 转发 <see cref="HttpContext" /> 到新的 HTTP 远程地址
|
||
/// </summary>
|
||
/// <param name="httpContext">
|
||
/// <see cref="HttpContext" />
|
||
/// </param>
|
||
/// <param name="httpMethod">转发方式</param>
|
||
/// <param name="requestUri">转发地址。若为空则尝试从请求标头 <c>X-Forward-To</c> 中获取目标地址。</param>
|
||
/// <param name="configure">自定义配置委托</param>
|
||
/// <param name="completionOption">
|
||
/// <see cref="HttpCompletionOption" />
|
||
/// </param>
|
||
/// <param name="forwardOptions">
|
||
/// <see cref="HttpContextForwardOptions" />
|
||
/// </param>
|
||
/// <typeparam name="TResult">转换的目标类型</typeparam>
|
||
/// <returns>
|
||
/// <see cref="HttpRemoteResult{TResult}" />
|
||
/// </returns>
|
||
public static HttpRemoteResult<TResult>? Forward<TResult>(this HttpContext? httpContext, HttpMethod httpMethod,
|
||
string? requestUri = null, Action<HttpRequestBuilder>? configure = null,
|
||
HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead,
|
||
HttpContextForwardOptions? forwardOptions = null) =>
|
||
Forward<TResult>(httpContext, httpMethod,
|
||
string.IsNullOrWhiteSpace(requestUri) ? null : new Uri(requestUri, UriKind.RelativeOrAbsolute), configure,
|
||
completionOption, forwardOptions);
|
||
|
||
/// <summary>
|
||
/// 转发 <see cref="HttpContext" /> 到新的 HTTP 远程地址
|
||
/// </summary>
|
||
/// <param name="httpContext">
|
||
/// <see cref="HttpContext" />
|
||
/// </param>
|
||
/// <param name="requestUri">转发地址。若为空则尝试从请求标头 <c>X-Forward-To</c> 中获取目标地址。</param>
|
||
/// <param name="configure">自定义配置委托</param>
|
||
/// <param name="completionOption">
|
||
/// <see cref="HttpCompletionOption" />
|
||
/// </param>
|
||
/// <param name="forwardOptions">
|
||
/// <see cref="HttpContextForwardOptions" />
|
||
/// </param>
|
||
/// <typeparam name="TResult">转换的目标类型</typeparam>
|
||
/// <returns>
|
||
/// <see cref="HttpRemoteResult{TResult}" />
|
||
/// </returns>
|
||
public static HttpRemoteResult<TResult>? Forward<TResult>(this HttpContext? httpContext, Uri? requestUri = null,
|
||
Action<HttpRequestBuilder>? configure = null,
|
||
HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead,
|
||
HttpContextForwardOptions? forwardOptions = null) =>
|
||
Forward<TResult>(httpContext, Helpers.ParseHttpMethod(httpContext?.Request.Method), requestUri,
|
||
configure, completionOption, forwardOptions);
|
||
|
||
/// <summary>
|
||
/// 转发 <see cref="HttpContext" /> 到新的 HTTP 远程地址
|
||
/// </summary>
|
||
/// <param name="httpContext">
|
||
/// <see cref="HttpContext" />
|
||
/// </param>
|
||
/// <param name="httpMethod">转发方式</param>
|
||
/// <param name="requestUri">转发地址。若为空则尝试从请求标头 <c>X-Forward-To</c> 中获取目标地址。</param>
|
||
/// <param name="configure">自定义配置委托</param>
|
||
/// <param name="completionOption">
|
||
/// <see cref="HttpCompletionOption" />
|
||
/// </param>
|
||
/// <param name="forwardOptions">
|
||
/// <see cref="HttpContextForwardOptions" />
|
||
/// </param>
|
||
/// <typeparam name="TResult">转换的目标类型</typeparam>
|
||
/// <returns>
|
||
/// <see cref="HttpRemoteResult{TResult}" />
|
||
/// </returns>
|
||
public static HttpRemoteResult<TResult>? Forward<TResult>(this HttpContext? httpContext, HttpMethod httpMethod,
|
||
Uri? requestUri = null, Action<HttpRequestBuilder>? 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<TResult>(httpRequestBuilder, completionOption, httpContext.RequestAborted);
|
||
|
||
// 根据配置选项将 HttpResponseMessage 信息转发到 HttpContext 中
|
||
ForwardResponseMessage(httpContext, result?.ResponseMessage, httpContextForwardBuilder.ForwardOptions);
|
||
|
||
return result;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 转发 <see cref="HttpContext" /> 到新的 HTTP 远程地址
|
||
/// </summary>
|
||
/// <param name="httpContext">
|
||
/// <see cref="HttpContext" />
|
||
/// </param>
|
||
/// <param name="requestUri">转发地址。若为空则尝试从请求标头 <c>X-Forward-To</c> 中获取目标地址。</param>
|
||
/// <param name="configure">自定义配置委托</param>
|
||
/// <param name="completionOption">
|
||
/// <see cref="HttpCompletionOption" />
|
||
/// </param>
|
||
/// <param name="forwardOptions">
|
||
/// <see cref="HttpContextForwardOptions" />
|
||
/// </param>
|
||
/// <typeparam name="TResult">转换的目标类型</typeparam>
|
||
/// <returns>
|
||
/// <see cref="HttpRemoteResult{TResult}" />
|
||
/// </returns>
|
||
public static Task<HttpRemoteResult<TResult>?> ForwardAsync<TResult>(this HttpContext? httpContext,
|
||
string? requestUri = null, Action<HttpRequestBuilder>? configure = null,
|
||
HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead,
|
||
HttpContextForwardOptions? forwardOptions = null) =>
|
||
ForwardAsync<TResult>(httpContext, Helpers.ParseHttpMethod(httpContext?.Request.Method),
|
||
string.IsNullOrWhiteSpace(requestUri) ? null : new Uri(requestUri, UriKind.RelativeOrAbsolute), configure,
|
||
completionOption, forwardOptions);
|
||
|
||
/// <summary>
|
||
/// 转发 <see cref="HttpContext" /> 到新的 HTTP 远程地址
|
||
/// </summary>
|
||
/// <param name="httpContext">
|
||
/// <see cref="HttpContext" />
|
||
/// </param>
|
||
/// <param name="httpMethod">转发方式</param>
|
||
/// <param name="requestUri">转发地址。若为空则尝试从请求标头 <c>X-Forward-To</c> 中获取目标地址。</param>
|
||
/// <param name="configure">自定义配置委托</param>
|
||
/// <param name="completionOption">
|
||
/// <see cref="HttpCompletionOption" />
|
||
/// </param>
|
||
/// <param name="forwardOptions">
|
||
/// <see cref="HttpContextForwardOptions" />
|
||
/// </param>
|
||
/// <typeparam name="TResult">转换的目标类型</typeparam>
|
||
/// <returns>
|
||
/// <see cref="HttpRemoteResult{TResult}" />
|
||
/// </returns>
|
||
public static Task<HttpRemoteResult<TResult>?> ForwardAsync<TResult>(this HttpContext? httpContext,
|
||
HttpMethod httpMethod, string? requestUri = null, Action<HttpRequestBuilder>? configure = null,
|
||
HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead,
|
||
HttpContextForwardOptions? forwardOptions = null) =>
|
||
ForwardAsync<TResult>(httpContext, httpMethod,
|
||
string.IsNullOrWhiteSpace(requestUri) ? null : new Uri(requestUri, UriKind.RelativeOrAbsolute), configure,
|
||
completionOption, forwardOptions);
|
||
|
||
/// <summary>
|
||
/// 转发 <see cref="HttpContext" /> 到新的 HTTP 远程地址
|
||
/// </summary>
|
||
/// <param name="httpContext">
|
||
/// <see cref="HttpContext" />
|
||
/// </param>
|
||
/// <param name="requestUri">转发地址。若为空则尝试从请求标头 <c>X-Forward-To</c> 中获取目标地址。</param>
|
||
/// <param name="configure">自定义配置委托</param>
|
||
/// <param name="completionOption">
|
||
/// <see cref="HttpCompletionOption" />
|
||
/// </param>
|
||
/// <param name="forwardOptions">
|
||
/// <see cref="HttpContextForwardOptions" />
|
||
/// </param>
|
||
/// <typeparam name="TResult">转换的目标类型</typeparam>
|
||
/// <returns>
|
||
/// <see cref="HttpRemoteResult{TResult}" />
|
||
/// </returns>
|
||
public static Task<HttpRemoteResult<TResult>?> ForwardAsync<TResult>(this HttpContext? httpContext,
|
||
Uri? requestUri = null, Action<HttpRequestBuilder>? configure = null,
|
||
HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead,
|
||
HttpContextForwardOptions? forwardOptions = null) =>
|
||
ForwardAsync<TResult>(httpContext, Helpers.ParseHttpMethod(httpContext?.Request.Method), requestUri,
|
||
configure, completionOption, forwardOptions);
|
||
|
||
/// <summary>
|
||
/// 转发 <see cref="HttpContext" /> 到新的 HTTP 远程地址
|
||
/// </summary>
|
||
/// <param name="httpContext">
|
||
/// <see cref="HttpContext" />
|
||
/// </param>
|
||
/// <param name="httpMethod">转发方式</param>
|
||
/// <param name="requestUri">转发地址。若为空则尝试从请求标头 <c>X-Forward-To</c> 中获取目标地址。</param>
|
||
/// <param name="configure">自定义配置委托</param>
|
||
/// <param name="completionOption">
|
||
/// <see cref="HttpCompletionOption" />
|
||
/// </param>
|
||
/// <param name="forwardOptions">
|
||
/// <see cref="HttpContextForwardOptions" />
|
||
/// </param>
|
||
/// <typeparam name="TResult">转换的目标类型</typeparam>
|
||
/// <returns>
|
||
/// <see cref="HttpRemoteResult{TResult}" />
|
||
/// </returns>
|
||
public static async Task<HttpRemoteResult<TResult>?> ForwardAsync<TResult>(this HttpContext? httpContext,
|
||
HttpMethod httpMethod, Uri? requestUri = null, Action<HttpRequestBuilder>? 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<TResult>(httpRequestBuilder, completionOption,
|
||
httpContext.RequestAborted).ConfigureAwait(false);
|
||
|
||
// 根据配置选项将 HttpResponseMessage 信息转发到 HttpContext 中
|
||
ForwardResponseMessage(httpContext, result?.ResponseMessage, httpContextForwardBuilder.ForwardOptions);
|
||
|
||
return result;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 创建 <see cref="HttpContextForwardBuilder" /> 实例
|
||
/// </summary>
|
||
/// <param name="httpContext">
|
||
/// <see cref="HttpContext" />
|
||
/// </param>
|
||
/// <param name="httpMethod">转发方式</param>
|
||
/// <param name="requestUri">转发地址。若为空则尝试从请求标头 <c>X-Forward-To</c> 中获取目标地址。</param>
|
||
/// <param name="forwardOptions">
|
||
/// <see cref="HttpContextForwardOptions" />
|
||
/// </param>
|
||
/// <returns>
|
||
/// <see cref="HttpContextForwardBuilder" />
|
||
/// </returns>
|
||
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);
|
||
|
||
/// <summary>
|
||
/// 创建 <see cref="HttpContextForwardBuilder" /> 实例
|
||
/// </summary>
|
||
/// <param name="httpContext">
|
||
/// <see cref="HttpContext" />
|
||
/// </param>
|
||
/// <param name="requestUri">转发地址。若为空则尝试从请求标头 <c>X-Forward-To</c> 中获取目标地址。</param>
|
||
/// <param name="forwardOptions">
|
||
/// <see cref="HttpContextForwardOptions" />
|
||
/// </param>
|
||
/// <returns>
|
||
/// <see cref="HttpContextForwardBuilder" />
|
||
/// </returns>
|
||
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);
|
||
|
||
/// <summary>
|
||
/// 创建 <see cref="HttpContextForwardBuilder" /> 实例
|
||
/// </summary>
|
||
/// <param name="httpContext">
|
||
/// <see cref="HttpContext" />
|
||
/// </param>
|
||
/// <param name="httpMethod">转发方式</param>
|
||
/// <param name="requestUri">转发地址。若为空则尝试从请求标头 <c>X-Forward-To</c> 中获取目标地址。</param>
|
||
/// <param name="forwardOptions">
|
||
/// <see cref="HttpContextForwardOptions" />
|
||
/// </param>
|
||
/// <returns>
|
||
/// <see cref="HttpContextForwardBuilder" />
|
||
/// </returns>
|
||
public static HttpContextForwardBuilder CreateForwardBuilder(this HttpContext? httpContext, HttpMethod httpMethod,
|
||
Uri? requestUri = null, HttpContextForwardOptions? forwardOptions = null) =>
|
||
new(httpContext, httpMethod, requestUri, forwardOptions);
|
||
|
||
/// <summary>
|
||
/// 创建 <see cref="HttpContextForwardBuilder" /> 实例
|
||
/// </summary>
|
||
/// <param name="httpContext">
|
||
/// <see cref="HttpContext" />
|
||
/// </param>
|
||
/// <param name="requestUri">转发地址。若为空则尝试从请求标头 <c>X-Forward-To</c> 中获取目标地址。</param>
|
||
/// <param name="forwardOptions">
|
||
/// <see cref="HttpContextForwardOptions" />
|
||
/// </param>
|
||
/// <returns>
|
||
/// <see cref="HttpContextForwardBuilder" />
|
||
/// </returns>
|
||
public static HttpContextForwardBuilder CreateForwardBuilder(this HttpContext? httpContext, Uri? requestUri = null,
|
||
HttpContextForwardOptions? forwardOptions = null) =>
|
||
new(httpContext, Helpers.ParseHttpMethod(httpContext?.Request.Method), requestUri, forwardOptions);
|
||
|
||
/// <summary>
|
||
/// 根据配置选项将 <see cref="HttpResponseMessage" /> 信息转发到 <see cref="HttpContext" /> 中
|
||
/// </summary>
|
||
/// <param name="httpContext">
|
||
/// <see cref="HttpContext" />
|
||
/// </param>
|
||
/// <param name="httpResponseMessage">
|
||
/// <see cref="HttpResponseMessage" />
|
||
/// </param>
|
||
/// <param name="forwardOptions">
|
||
/// <see cref="HttpContextForwardOptions" />
|
||
/// </param>
|
||
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);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 转发 HTTP 标头
|
||
/// </summary>
|
||
/// <param name="httpResponse">
|
||
/// <see cref="HttpResponse" />
|
||
/// </param>
|
||
/// <param name="httpHeaders">
|
||
/// <see cref="HttpHeaders" />
|
||
/// </param>
|
||
/// <param name="forwardOptions">
|
||
/// <see cref="HttpContextForwardOptions" />
|
||
/// </param>
|
||
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();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 初始化转发所需的服务
|
||
/// </summary>
|
||
/// <param name="httpContext">
|
||
/// <see cref="HttpContext" />
|
||
/// </param>
|
||
/// <param name="httpMethod">转发方式</param>
|
||
/// <param name="requestUri">转发地址。若为空则尝试从请求标头 <c>X-Forward-To</c> 中获取目标地址。</param>
|
||
/// <param name="configure">自定义配置委托</param>
|
||
/// <param name="forwardOptions">
|
||
/// <see cref="HttpCompletionOption" />
|
||
/// </param>
|
||
/// <returns>
|
||
/// <see cref="Tuple" />
|
||
/// </returns>
|
||
internal static (HttpContextForwardBuilder httpContextForwardBuilder, HttpRequestBuilder httpRequestBuilder,
|
||
IHttpRemoteService httpRemoteService) PrepareForwardService(HttpContext httpContext, HttpMethod httpMethod,
|
||
Uri? requestUri, Action<HttpRequestBuilder>? 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<IHttpRemoteService>();
|
||
|
||
return (httpContextForwardBuilder, httpRequestBuilder, httpRemoteService);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 初始化转发所需的服务
|
||
/// </summary>
|
||
/// <param name="httpContext">
|
||
/// <see cref="HttpContext" />
|
||
/// </param>
|
||
/// <param name="httpMethod">转发方式</param>
|
||
/// <param name="requestUri">转发地址。若为空则尝试从请求标头 <c>X-Forward-To</c> 中获取目标地址。</param>
|
||
/// <param name="configure">自定义配置委托</param>
|
||
/// <param name="forwardOptions">
|
||
/// <see cref="HttpCompletionOption" />
|
||
/// </param>
|
||
/// <returns>
|
||
/// <see cref="Tuple" />
|
||
/// </returns>
|
||
internal static async
|
||
Task<(HttpContextForwardBuilder httpContextForwardBuilder, HttpRequestBuilder httpRequestBuilder,
|
||
IHttpRemoteService
|
||
httpRemoteService)> PrepareForwardServiceAsync(HttpContext httpContext, HttpMethod httpMethod,
|
||
Uri? requestUri,
|
||
Action<HttpRequestBuilder>? 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<IHttpRemoteService>();
|
||
|
||
return (httpContextForwardBuilder, httpRequestBuilder, httpRemoteService);
|
||
}
|
||
} |