Net Core中 使用Middleware 实现反向代理
- 2019 年 11 月 7 日
- 筆記
有这样的一个需求,我们要拦截某些特定的请求,并将它们重新定向到另一台服务器中,然而客户端并不知情。
在NetCore中我们可以用中间件来实现,
首先创建项目:
我这里只有2.1 Version 的
添加ProxyMiddleware
ProxyMiddleware内容如下:
代码不多有兴趣的朋友可以调试一下。这里还可以有很多的方向扩展。
public class ProxyMiddleware { private static readonly HttpClient _httpClient = new HttpClient(); private readonly RequestDelegate _nextRequestDelegate; private static readonly Uri _targetUri = new Uri("https://www.cnblogs.com/"); public ProxyMiddleware(RequestDelegate nextMiddleware) { _nextRequestDelegate = nextMiddleware; } public async Task Invoke(HttpContext context) { bool validateUri = false; if (context.Request.Path.StartsWithSegments("/api/values", out var Path)) { validateUri = true; } if (validateUri == true) { var targetRequestMessage = CreateTargetMessage(context); using (var responseMessage = await _httpClient.SendAsync(targetRequestMessage)) { context.Response.StatusCode = (int)responseMessage.StatusCode; CloneResponseHeadersIntoContext(context, responseMessage); await responseMessage.Content.CopyToAsync(context.Response.Body); } return; } await _nextRequestDelegate(context); } private void CloneRequestContentAndHeaders(HttpContext context, HttpRequestMessage requestMessage) { foreach (var header in context.Request.Headers) { requestMessage.Content?.Headers.TryAddWithoutValidation(header.Key, header.Value.ToArray()); } } private HttpRequestMessage CreateTargetMessage(HttpContext context) { var requestMessage = new HttpRequestMessage(); CloneRequestContentAndHeaders(context, requestMessage); requestMessage.RequestUri = _targetUri; requestMessage.Headers.Host = _targetUri.Host; requestMessage.Method = new HttpMethod(context.Request.Method); return requestMessage; } private void CloneResponseHeadersIntoContext(HttpContext context, HttpResponseMessage responseMessage) { foreach (var header in responseMessage.Headers) { context.Response.Headers[header.Key] = header.Value.ToArray(); } foreach (var header in responseMessage.Content.Headers) { context.Response.Headers[header.Key] = header.Value.ToArray(); } context.Response.Headers.Remove("Transfer-Encoding"); } }
添加管道
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseMiddleware<ProxyMiddleware>(); app.UseMvc(); }
运行结果:
大家可以注意浏览器网址,以及显示的内容就可以了,(样式没了)
代码解释:
所有的描述在代码中,这里我只是标出这点代码的重点
创建静态HttpClient连接,减少连接池数量
private static readonly HttpClient _httpClient = new HttpClient();
private readonly RequestDelegate _nextRequestDelegate;
新的目标服务器 private static readonly Uri _targetUri = new Uri("https://www.cnblogs.com/"); public ProxyMiddleware(RequestDelegate nextMiddleware) { _nextRequestDelegate = nextMiddleware; }
所有的工作将由 Invoke执行
public async Task Invoke(HttpContext context) { bool validateUri = false; if (context.Request.Path.StartsWithSegments("/api/values", out var Path)) { validateUri = true; } if (validateUri == true) { var targetRequestMessage = CreateTargetMessage(context); using (var responseMessage = await _httpClient.SendAsync(targetRequestMessage)) { context.Response.StatusCode = (int)responseMessage.StatusCode; CloneResponseHeadersIntoContext(context, responseMessage); await responseMessage.Content.CopyToAsync(context.Response.Body); } return; } await _nextRequestDelegate(context); }
private void CloneResponseHeadersIntoContext(HttpContext context, HttpResponseMessage responseMessage) { foreach (var header in responseMessage.Headers) { context.Response.Headers[header.Key] = header.Value.ToArray(); } foreach (var header in responseMessage.Content.Headers) { context.Response.Headers[header.Key] = header.Value.ToArray(); }
这里有一个坑大家注意了,有兴趣的同学可以调查研究一下,要是介绍的话可以单独开一篇了 context.Response.Headers.Remove("Transfer-Encoding"); }
有不足之处 希望大家指出相互学习。