.NET Core如何将HttpRequest推入中间件管道?跨网受限场景方案咨询
解决方案:基于WebSocket桥接的ASP.NET Core请求管道触发方案
针对你的安全规则限制(DMZ服务器禁止主动连接内网、外网服务器不得暴露用户数据),可以通过内网主动发起WebSocket长连接到DMZ服务器构建桥接,再通过手动创建HttpContext触发ASP.NET Core中间件管道的方式实现需求,同时完整保留WebSocket功能。
核心架构思路
- 内网服务器主动向外网DMZ服务器发起WebSocket长连接(规避DMZ主动连内网的限制)
- 外网服务器接收客户端的HTTP/WebSocket请求后,将请求数据序列化后通过已建立的WebSocket桥接推送给内网服务器
- 内网服务器手动构建
HttpContext并触发中间件管道处理请求,处理完成后将响应回传给外网服务器 - 外网服务器把响应返回给客户端,全程不存储或暴露用户敏感数据
手动触发ASP.NET Core中间件管道的实现
ASP.NET Core的中间件管道并不依赖原生HTTP请求触发,可通过IHttpContextFactory创建自定义上下文,手动调用管道委托。
1. 定义请求/响应传输模型
用于在WebSocket桥接中传递请求和响应的核心数据:
public class ProxyRequest { public string Method { get; set; } public string Path { get; set; } public string QueryString { get; set; } public Dictionary<string, string> Headers { get; set; } public string Body { get; set; } public bool IsWebSocketUpgrade { get; set; } // 标识是否为WebSocket升级请求 } public class ProxyResponse { public int StatusCode { get; set; } public Dictionary<string, string> Headers { get; set; } public string Body { get; set; } }
2. 内网服务器的请求处理器
注入IHttpContextFactory和中间件管道委托,手动构建上下文并触发管道:
public class BridgeRequestProcessor { private readonly IHttpContextFactory _httpContextFactory; private readonly RequestDelegate _requestPipeline; private readonly IServiceScopeFactory _scopeFactory; public BridgeRequestProcessor(IHttpContextFactory httpContextFactory, RequestDelegate requestPipeline, IServiceScopeFactory scopeFactory) { _httpContextFactory = httpContextFactory; _requestPipeline = requestPipeline; _scopeFactory = scopeFactory; } public async Task<ProxyResponse> ProcessRequestAsync(ProxyRequest proxyRequest) { // 为每个请求创建独立的服务范围,保证隔离性 using var scope = _scopeFactory.CreateScope(); var httpContext = _httpContextFactory.Create(scope.ServiceProvider); // 配置请求属性 httpContext.Request.Method = proxyRequest.Method; httpContext.Request.Path = proxyRequest.Path; httpContext.Request.QueryString = new QueryString(proxyRequest.QueryString); // 复制请求头 foreach (var header in proxyRequest.Headers) { httpContext.Request.Headers.TryAdd(header.Key, header.Value); } // 写入请求正文 if (!string.IsNullOrEmpty(proxyRequest.Body)) { var bodyBytes = Encoding.UTF8.GetBytes(proxyRequest.Body); await httpContext.Request.Body.WriteAsync(bodyBytes); httpContext.Request.Body.Position = 0; } // 手动触发中间件管道 await _requestPipeline(httpContext); // 读取并封装响应 var response = new ProxyResponse { StatusCode = (int)httpContext.Response.StatusCode, Headers = httpContext.Response.Headers.ToDictionary(h => h.Key, h => h.Value.ToString()) }; httpContext.Response.Body.Position = 0; using var reader = new StreamReader(httpContext.Response.Body); response.Body = await reader.ReadToEndAsync(); return response; } }
3. WebSocket请求的透明转发
对于WebSocket请求,无需经过中间件管道,直接在外网和内网服务器之间转发WebSocket帧:
// 外网服务器:处理客户端WebSocket连接并转发到桥接连接 public async Task RelayWebSocketAsync(WebSocket clientSocket, WebSocket bridgeSocket, CancellationToken ct) { var buffer = new byte[4096]; var clientReceive = clientSocket.ReceiveAsync(new ArraySegment<byte>(buffer), ct); var bridgeReceive = bridgeSocket.ReceiveAsync(new ArraySegment<byte>(buffer), ct); while (!ct.IsCancellationRequested && clientSocket.State == WebSocketState.Open && bridgeSocket.State == WebSocketState.Open) { var completed = await Task.WhenAny(clientReceive, bridgeReceive); if (completed == clientReceive) { var result = await clientReceive; await bridgeSocket.SendAsync(new ArraySegment<byte>(buffer, 0, result.Count), result.MessageType, result.EndOfMessage, ct); clientReceive = clientSocket.ReceiveAsync(new ArraySegment<byte>(buffer), ct); } else { var result = await bridgeReceive; await clientSocket.SendAsync(new ArraySegment<byte>(buffer, 0, result.Count), result.MessageType, result.EndOfMessage, ct); bridgeReceive = bridgeSocket.ReceiveAsync(new ArraySegment<byte>(buffer), ct); } } }
通用优化建议
- 序列化优化:用Protobuf替代JSON序列化请求/响应数据,提升传输性能和压缩率
- 连接保活与重连:在内网服务器中实现WebSocket连接的自动重连机制,配合心跳包维持长连接
- 请求路由:在外网服务器中添加简单的路由逻辑,将不同路径的请求转发到对应内网服务的桥接连接
- 日志与监控:在桥接两端添加请求日志、连接状态监控,便于排查问题
内容的提问来源于stack exchange,提问作者Seller Weller
相关产品推荐
相关产品推荐

