Net Core中 使用Middleware 實現反向代理


有這樣的一個需求,我們要攔截某些特定的請求,並將它們重新定向到另一台服務器中,然而客戶端並不知情。

在NetCore中我們可以用中間件來實現,

為什么要使用反向代理

  反向代理一般在下面的場景中進行使用:

  • 負載均衡:

    反向代理。它可以根據一些特定算法在一組相同的服務器之間分配請求負載,從而為系統的可伸縮性和可用性提供支持。

  • 網址重寫:

    可以將無法更改的網絡路徑隱藏在反向代理后面。

  • 靜態內容投放:

    反向代理服務器可以充當Web服務器。這使您可以將它們用於提供靜態內容,例如HTML頁面,JavaScript腳本,圖像和其他文件,同時將對動態內容的請求轉發到專用服務器。這是一種基於內容類型的負載平衡。

  • API網關:

    在具有微服務架構的系統中,您有多個服務器通過其API提供不同的服務。您可以使用反向代理來公開服務器API組合的單個入口點。

  • 多個網站合並:

    使多個網站使用共同的一個入口(網站),和微服務中的網關作用差不多。

首先創建項目:

  我這里只有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"); }

 

有不足之處 希望大家指出相互學習。

        

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM