Popular blog tags

How to creating a Reverse Proxy middleware in ASP.NET core

Published

This post is about creating a  Reverse Proxy middleware in ASP.NET Core.

 

.Net 3.x

We can accomplish this in three easy steps.

Step 1: Create a Reverse Proxy middleware class

First we need to create a ReverseProxyMiddleware object to hold information about each page URL.

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.Primitives;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;

namespace Miniblog.Core.Middleware
{

    public class ReverseProxyMiddleware
    {
        private static readonly HttpClient _httpClient = new HttpClient();
        private readonly RequestDelegate _nextMiddleware;

        public ReverseProxyMiddleware(RequestDelegate nextMiddleware)
        {
            _nextMiddleware = nextMiddleware;
        }

        public async Task Invoke(HttpContext context)
        {
            var targetUri = BuildTargetUri(context.Request);

            if (targetUri != null)
            {
                var targetRequestMessage = CreateTargetMessage(context, targetUri);

                using (var responseMessage = await _httpClient.SendAsync(targetRequestMessage, HttpCompletionOption.ResponseHeadersRead, context.RequestAborted))
                {
                    context.Response.StatusCode = (int)responseMessage.StatusCode;

                    CopyFromTargetResponseHeaders(context, responseMessage);

                    await ProcessResponseContent(context, responseMessage);
                }

                return;
            }

            await _nextMiddleware(context);
        }

        private async Task ProcessResponseContent(HttpContext context, HttpResponseMessage responseMessage)
        {
            var content = await responseMessage.Content.ReadAsByteArrayAsync();

            if (IsContentOfType(responseMessage, "text/html") || IsContentOfType(responseMessage, "text/javascript"))
            {
                var stringContent = Encoding.UTF8.GetString(content);
                var newContent = stringContent.Replace("https://www.google.com", "/google")
                    .Replace("https://www.gstatic.com", "/googlestatic")
                    .Replace("https://docs.google.com/forms", "/googleforms");
                await context.Response.WriteAsync(newContent, Encoding.UTF8);
            }
            else
            {
                await context.Response.Body.WriteAsync(content);
            }
        }

        private bool IsContentOfType(HttpResponseMessage responseMessage, string type)
        {
            var result = false;

            if (responseMessage.Content?.Headers?.ContentType != null)
            {
                result = responseMessage.Content.Headers.ContentType.MediaType == type;
            }

            return result;
        }

        private HttpRequestMessage CreateTargetMessage(HttpContext context, Uri targetUri)
        {
            var requestMessage = new HttpRequestMessage();
            CopyFromOriginalRequestContentAndHeaders(context, requestMessage);

            targetUri = new Uri(QueryHelpers.AddQueryString(targetUri.OriginalString, new Dictionary<string, string>() { { "entry.1884265043", "John Doe" } }));

            requestMessage.RequestUri = targetUri;
            requestMessage.Headers.Host = targetUri.Host;
            requestMessage.Method = GetMethod(context.Request.Method);

            return requestMessage;
        }

        private void CopyFromOriginalRequestContentAndHeaders(HttpContext context, HttpRequestMessage requestMessage)
        {
            var requestMethod = context.Request.Method;

            if (!HttpMethods.IsGet(requestMethod) &&
                !HttpMethods.IsHead(requestMethod) &&
                !HttpMethods.IsDelete(requestMethod) &&
                !HttpMethods.IsTrace(requestMethod))
            {
                var streamContent = new StreamContent(context.Request.Body);
                requestMessage.Content = streamContent;
            }

            foreach (var header in context.Request.Headers)
            {
                requestMessage.Content?.Headers.TryAddWithoutValidation(header.Key, header.Value.ToArray());
            }
        }

        private void CopyFromTargetResponseHeaders(HttpContext context, HttpResponseMessage responseMessage)
        {
            foreach (var header in responseMessage.Headers)
            {
                context.Response.Headers[header.Key] = header.Value.ToArray();
            }
            //set each one of these headers
            foreach (var header in responseMessage.Content.Headers)
            {
                context.Response.Headers[header.Key] = header.Value.ToArray();
            }
            context.Response.Headers.Remove("transfer-encoding");
        }
        private static HttpMethod GetMethod(string method)
        {
            if (HttpMethods.IsDelete(method)) return HttpMethod.Delete;
            if (HttpMethods.IsGet(method)) return HttpMethod.Get;
            if (HttpMethods.IsHead(method)) return HttpMethod.Head;
            if (HttpMethods.IsOptions(method)) return HttpMethod.Options;
            if (HttpMethods.IsPost(method)) return HttpMethod.Post;
            if (HttpMethods.IsPut(method)) return HttpMethod.Put;
            if (HttpMethods.IsTrace(method)) return HttpMethod.Trace;
            return new HttpMethod(method);
        }

        private Uri BuildTargetUri(HttpRequest request)
        {
            Uri targetUri = null;
            PathString remainingPath;

            if (request.Path.StartsWithSegments("/googleforms", out remainingPath))
            {
                targetUri = new Uri("https://docs.google.com/forms" + remainingPath);
            }

            if (request.Path.StartsWithSegments("/google", out remainingPath))
            {
                targetUri = new Uri("https://www.google.com" + remainingPath);
            }

            if (request.Path.StartsWithSegments("/googlestatic", out remainingPath))
            {
                targetUri = new Uri(" https://www.gstatic.com" + remainingPath);
            }

            return targetUri;
        }
    }
}

Step 2: Registering the Reverse Proxy middleware in Startup

The next step is to use the ReverseProxyMiddleware class in our MVC application .So, open the Startup.cs file and modify the Configure() method as shown in the following example:

path:/Startup.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Http;

...


public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
  if (env.IsDevelopment())
  {
    app.UseDeveloperExceptionPage();
  }

  app.UseMiddleware<ReverseProxyMiddleware>();

  app.Run(async (context) =>
  {
    await context.Response.WriteAsync("<a href='/googleforms/d/e/1FAIpQLSdJwmxHIl_OCh-CI1J68G1EVSr9hKaYFLh3dHh8TLnxjxCJWw/viewform?hl=en'>Register to receive a T-shirt</a>");
  });
}

Note: The following reference needs added to your project.json file for the code above to work in ASP.NET Core.

"System.Xml.XmlDocument": "4.0.1-beta-23516"

 

useful links

微软项目 ReverseProxy ,叫做 YARP(A Reverse Proxy)

https://github.com/microsoft/reverse-proxy

YARP doc:https://microsoft.github.io/reverse-proxy/articles/getting-started.html

 

https://stackoverflow.com/questions/42000362/creating-a-proxy-to-another-web-api-with-asp-net-core

https://github.com/twitchax/AspNetCore.Proxy

CREATING A SIMPLE PROXY SERVER MIDDLEWARE IN ASP.NET CORE

 a basic implementation of Proxy library for ASP.NET Core.It basically implements a middleware that reads the request received, creates a copy from it and sends it back to a configured service, reads the response from the service and sends it back to the caller.

https://github.com/aspnet/AspLabs/tree/master/src/Proxy/src

https://github.com/twitchax/AspNetCore.Proxy

https://stackoverflow.com/questions/42000362/creating-a-proxy-to-another-web-api-with-asp-net-core/54149906#54149906

 

.net core使用HttpClient发送代理请求_程序内抓包_Fiddler抓包

 

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

https://www.cnblogs.com/szlblog/p/11813770.html

C++实现 http和https的反向代理程序 

说明一下两个代理方式 http和https代理。(含请求报文图解)

https://www.cnblogs.com/cjdty/p/13231111.html