Net Core中 使用Middleware 实现反向代理

2019-11-07 来源: 乐途 发布在  https://www.cnblogs.com/szlblog/p/11813770.html

有这样的一个需求,我们要拦截某些特定的请求,并将它们重新定向到另一台服务器中,然而客户端并不知情。

在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");
        }

有不足之处 希望大家指出相互学习。

        

相关文章