6.16HttpContext 转发进阶
框架为 HttpContext 提供了多种扩展方法,旨在满足各种场景下的 HTTP 请求转发需求。
HttpContext 转发扩展方法#
框架为 HttpContext 提供了多种扩展方法,旨在满足各种场景下的 HTTP 请求转发需求。
// 返回 HttpResponseMessage 对象httpContext.Forward(requestUri, configure, completionOption, forwardOptions); // 自动获取 HttpContext.Request.Method 方法进行设置httpContext.Forward(httpMethod, requestUri, configure, completionOption, forwardOptions);await httpContext.ForwardAsync(requestUri, configure, completionOption, forwardOptions); // 自动获取 HttpContext.Request.Method 方法进行设置await httpContext.ForwardAsync(httpMethod, requestUri, configure, completionOption, forwardOptions);// 返回 HttpRemoteResult<T>httpContext.Forward<T>(requestUri, configure, completionOption, forwardOptions); // 自动获取 HttpContext.Request.Method 方法进行设置httpContext.Forward<T>(httpMethod, requestUri, configure, completionOption, forwardOptions);await httpContext.ForwardAsync<T>(requestUri, configure, completionOption, forwardOptions); // 自动获取 HttpContext.Request.Method 方法进行设置await httpContext.ForwardAsync<T>(httpMethod, requestUri, configure, completionOption, forwardOptions);// 返回目标 T 类型httpContext.ForwardAs<T>(requestUri, configure, completionOption, forwardOptions); // 自动获取 HttpContext.Request.Method 方法进行设置httpContext.ForwardAs<T>(httpMethod, requestUri, configure, completionOption, forwardOptions);await httpContext.ForwardAsAsync<T>(requestUri, configure, completionOption, forwardOptions); // 自动获取 HttpContext.Request.Method 方法进行设置await httpContext.ForwardAsAsync<T>(httpMethod, requestUri, configure, completionOption, forwardOptions);// 返回字符串类型httpContext.ForwardAsString(requestUri, configure, completionOption, forwardOptions); // 自动获取 HttpContext.Request.Method 方法进行设置httpContext.ForwardAsString(httpMethod, requestUri, configure, completionOption, forwardOptions);await httpContext.ForwardAsStringAsync(requestUri, configure, completionOption, forwardOptions); // 自动获取 HttpContext.Request.Method 方法进行设置await httpContext.ForwardAsStringAsync(httpMethod, requestUri, configure, completionOption, forwardOptions);// 返回字节数组类型httpContext.ForwardAsByteArray(requestUri, configure, completionOption, forwardOptions); // 自动获取 HttpContext.Request.Method 方法进行设置httpContext.ForwardAsByteArray(httpMethod, requestUri, configure, completionOption, forwardOptions);await httpContext.ForwardAsByteArrayAsync(requestUri, configure, completionOption, forwardOptions); // 自动获取 HttpContext.Request.Method 方法进行设置await httpContext.ForwardAsByteArrayAsync(httpMethod, requestUri, configure, completionOption, forwardOptions);// 返回 Stream 类型httpContext.ForwardAsStream(requestUri, configure, completionOption, forwardOptions); // 自动获取 HttpContext.Request.Method 方法进行设置httpContext.ForwardAsStream(httpMethod, requestUri, configure, completionOption, forwardOptions);await httpContext.ForwardAsStreamAsync(requestUri, configure, completionOption, forwardOptions); // 自动获取 HttpContext.Request.Method 方法进行设置await httpContext.ForwardAsStreamAsync(httpMethod, requestUri, configure, completionOption, forwardOptions);// 返回 IActionResult 类型httpContext.ForwardAsResult(requestUri, configure, completionOption, forwardOptions); // 自动获取 HttpContext.Request.Method 方法进行设置httpContext.ForwardAsResult(httpMethod, requestUri, configure, completionOption, forwardOptions);await httpContext.ForwardAsResultAsync(requestUri, configure, completionOption, forwardOptions); // 自动获取 HttpContext.Request.Method 方法进行设置await httpContext.ForwardAsResultAsync(httpMethod, requestUri, configure, completionOption, forwardOptions);// 返回 object 类型httpContext.ForwardAs(resultType, requestUri, configure, completionOption, forwardOptions); // 自动获取 HttpContext.Request.Method 方法进行设置httpContext.ForwardAs(resultType, httpMethod, requestUri, configure, completionOption, forwardOptions);await httpContext.ForwardAsAsync(resultType, requestUri, configure, completionOption, forwardOptions); // 自动获取 HttpContext.Request.Method 方法进行设置await httpContext.ForwardAsAsync(resultType, httpMethod, requestUri, configure, completionOption, forwardOptions);HttpContext 转发的优势#
在对接第三方 API 接口时,通常的做法是创建一个入口程序,并在其中调用 HTTP 远程请求服务来发送请求到指定的第三方接口。假设第三方接口的控制器定义如下:
[ApiController][Route("[controller]/[action]")]public class VendorController : ControllerBase{ [HttpPost] public VendorModel Add(VendorModel model) { return model; }}传统的做法是使用 HTTP 远程请求服务来发送请求,例如:
[ApiController][Route("[controller]/[action]")]public class YourController(IHttpRemoteService httpRemoteService) : ControllerBase{ [HttpPost] public async Task<VendorModel> AddVendorAsync() { return await httpRemoteService.SendAsync<VendorModel>( HttpRequestBuilder.Post("https://www.furion.net/vendor/add") .SetJsonContent(new VendorModel()) ); }}然而,使用 HttpContext 转发功能后,我们可以简化代码,只需创建与第三方接口一致的接口控制器声明,例如 VendorController 的 Add 方法,如下所示:
[ApiController][Route("[controller]/[action]")]public class YourController(IHttpContextAccessor httpContextAccessor) : ControllerBase{ [HttpPost] public async Task<VendorModel> AddAsync(VendorModel model) // 同步和异步都行 { // 自动转发 model,无需任何设置 return await httpContextAccessor.Context .ForwardAsync<VendorModel>("https://www.furion.net/vendor/add"); }}通过这种方式,代码变得更加简洁明了,无需手动构建和发送 HTTP 请求,而是利用 HttpContext 转发功能直接调用第三方接口。
HttpContext 转发在微服务中的应用#
在微服务架构中,HttpContext 转发功能展现出了极大的价值。微服务间的通信通常依赖于 HTTP 或 gRPC,其中 HTTP 由于其出色的兼容性而被广泛应用。采用 HttpContext 转发不仅能够简化 HTTP 请求的代码量,还能使代码结构更加清晰,便于维护。对于大型项目或团队协作项目,这一优势尤为明显。
借助 HttpContext 转发功能,我们可以实现动态的请求分发。通过应用某种权重算法,系统能够自动将请求转发至不同的服务器,从而实现负载均衡和故障转移。例如:
[ApiController][Route("[controller]/[action]")]public class YourController(IHttpContextAccessor httpContextAccessor) : ControllerBase{ [HttpPost] public async Task<VendorModel> AddAsync(VendorModel model) { string targetUrl = "https://furion.net/"; // 默认服务器地址 // 根据某种算法(如负载均衡策略)选择目标服务器地址 // 在微服务架构中,这通常由服务注册与发现机制来确定 // 下面的代码仅为模拟示例 if (条件1) { targetUrl = "https://s1.furion.net/"; // s1 服务器地址 } else if (条件2) { targetUrl = "https://s2.furion.net/"; // s2 服务器地址 } else if (条件3) { targetUrl = "https://s3.furion.net/"; // s3 服务器地址 } return await _httpContextAccessor.ForwardAsync<VendorModel>(targetUrl); }}此外,HttpContext 转发功能还使得构建网关中心成为可能。所有外部请求都可以先发送到网关中心,由网关进行鉴权、限流等处理后,再转发至目标服务。这极大地提升了系统的安全性和可管理性。总之,HttpContext 转发是微服务架构中一个不可或缺的组件,它为实现高效、灵活的微服务通信提供了有力支持。
转发忽略标头说明#
在使用 HttpContext 转发功能时,系统会自动忽略以下请求与响应标头,以确保转发的有效性和准确性:
-
请求标头中将被忽略的:
X-Forward-ToHostContent-Length
-
响应标头中将被忽略的:
Content-TypeTransfer-EncodingKeep-AliveUpgradeProxy-Connection
若需添加更多忽略请求或响应标头的设置,可通过 HttpContextForwardOptions 进行配置。以下是两种配置方式:
- 全局设置:
在项目的 Startup.cs 或 Program.cs 文件中,注册并配置该服务:
services.Configure<HttpContextForwardOptions>(options =>{ // 忽略在转发时需要跳过的请求标头列表 options.IgnoreRequestHeaders = ["Framework"]; // 忽略在转发时需要跳过的响应标头列表 options.IgnoreResponseHeaders = ["Content-Length"];});- 单次转发设置:
在单次转发时,可传递 HttpContextForwardOptions 进行配置:
httpContext.ForwardAsResult("https://furion.net", forwardOptions: new HttpContextForwardOptions{ // 忽略在转发时需要跳过的请求标头列表 options.IgnoreRequestHeaders = ["Framework"]; // 忽略在转发时需要跳过的响应标头列表 options.IgnoreResponseHeaders = ["Content-Length"];});ForwardAttribute 转发特性#
为了简化转发操作,框架提供了便捷的 [Forward] 控制器操作转发特性。相较于手动调用 HttpContext 的 Forward 扩展方法,该特性显著减少了重复的硬编码工作。以下是使用 [Forward] 特性的示例代码:
[ApiController][Route("[controller]/[action]")]public class GetStartController : ControllerBase{ /// <summary> /// 转发代理到网站 /// </summary> /// <returns></returns> [HttpGet] [ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)] // 禁用浏览器缓存 [Forward("https://github.com", AllowedHosts = ["*"])] public Task<IActionResult?> ForwardToWebSite() { throw new NotImplementedException(); } /// <summary> /// 转发代理到图片 /// </summary> /// <returns></returns> [HttpGet] [ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)] // 禁用浏览器缓存 [Forward("https://img-s-msn-com.akamaized.net/tenant/amp/entityid/AA1u7RJI.img?w=584&h=326&m=6", AllowedHosts = ["*"])] public Task<IActionResult?> ForwardToImage() { throw new NotImplementedException(); } /// <summary> /// 转发代理到文件 /// </summary> /// <returns></returns> [HttpGet] [ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)] // 禁用浏览器缓存 [Forward("https://download.visualstudio.microsoft.com/download/pr/a17b907f-8457-45a8-90db-53f2665ee49e/49bccd33593ebceb2847674fe5fd768e/aspnetcore-runtime-8.0.10-win-x64.exe", AllowedHosts = ["*"])] public Task<IActionResult?> ForwardToDownload() { throw new NotImplementedException(); } /// <summary> /// 转发代理到表单 /// </summary> /// <param name="id"></param> /// <param name="model"></param> /// <returns></returns> [HttpPost] [Forward("https://localhost:7044/HttpRemote/AddForm", AllowedHosts = ["*"])] public Task<YourRemoteFormResult?> ForwardToForm(int id, [FromForm] YourRemoteFormModel model) { throw new NotImplementedException(); } /// <summary> /// 转发代理到字符串 /// </summary> /// <returns></returns> [HttpGet] [Forward("https://localhost:7044/GetStart/PostRawString", AllowedHosts = ["*"])] public Task<string> ForwardToString() { throw new NotImplementedException(); } /// <summary> /// 转发代理到无返回值 /// </summary> /// <returns></returns> [HttpGet] [Forward("https://localhost:7044/GetStart/PostRawString", AllowedHosts = ["*"])] public Task ForwardToVoid() { throw new NotImplementedException(); }}在上述代码中,我们只需为需要转发的控制器操作添加 [Forward] 特性,并指定目标 URL。框架会自动处理转发逻辑,因此方法体内无需编写任何实现代码(通常抛出 NotImplementedException 以表明这是一个由框架自动处理的转发操作)。这种方式在微服务应用中尤为便捷,极大地简化了代码编写和维护工作。
ForwardAttribute 包含以下属性:
- 属性:
RequestUri:转发地址(string类型)。Method:转发方式,若未设置,则自动采用当前请求方式作为转发方式(HttpMethod类型)。HttpClientName:HttpClient实例的配置名称,默认值为null(string类型)。CompletionOption:指示响应内容操作方式,默认值为ResponseHeadersRead(HttpCompletionOption类型)。AllowedHosts:允许转发的目标主机白名单(string[]?类型)。
用于防范 服务端请求伪造(SSRF) 攻击。仅当目标地址的主机(含端口、协议)与列表中的某一项匹配时,转发才会被允许。
支持的格式(匹配均忽略大小写):"furion.net"– 仅主机名,匹配任意协议(http/https)的默认端口(80/443)。"furion.net:8080"– 主机+端口,匹配任意协议的指定端口。"furion.net:*"– 主机+端口通配符,匹配任意协议下的任意端口。"https://furion.net"– 协议+主机,只匹配指定协议的默认端口。"http://furion.net:8080"– 协议+主机+端口,精确匹配。"https://furion.net:*"– 协议+主机+端口通配符,只匹配指定协议的任意端口。"*"– 全局通配符,允许任意主机和协议(完全绕过验证,高风险)。
若未配置或为空,所有通过X-Forward-To请求头指定的目标地址都将被拒绝,以防止未经授权的转发。建议尽可能使用精确规则,仅对完全可信的来源开放通配符。
WithQueryParameters:是否转发查询参数(URL参数),默认值为true(bool类型)。WithRequestHeaders:是否转发请求标头,默认值为true(bool类型)。WithResponseStatusCode:是否转发响应状态码,默认值为true(bool类型)。WithResponseHeaders:是否转发响应标头,默认值为true(bool类型)。WithResponseContentHeaders:是否转发响应内容标头,默认值为true(bool类型)。ResetHostRequestHeader:是否重新设置Host请求标头,默认值为false(bool类型)。IgnoreQueryParameters:忽略在转发时需要跳过的查询参数(URL参数)列表(string[]?类型)。IgnoreRequestHeaders:忽略在转发时需要跳过的请求标头列表(string[]?类型)。IgnoreResponseHeaders:忽略在转发时需要跳过的响应标头列表(string[]?类型)。