ASP.NET Core 奇技淫巧之接口代理转发的实现

 更新时间:2021年9月22日 10:00  点击:1761

前言

先讲讲本文的开发背景吧..

在如今前后端分离的大背景下,咱的客户又有要求啦~

要前后端分离~ 然因为种种原因..没办法用用纯前端的框架(其实是学习成本高,又没钱请前端开发人员)...

所以最终决定了一种方案..

那就是采用MVC(只处理前端视图层,单纯是为了托管在.net core上)+Webapi的方式来实现前后端分离(讲真,很奇葩)..

那么问题就随之而来了.

现在主流的前端框架都是托管在nodejs上,是通过axios来访问后端API,可以通过配置axios的代理配置(proxyTable)来实现跨域访问.

那么我们的JS运行在MVC上,托管在.net core上..那咋办呢?..没有现成的转发轮子..我们只有自己造了..

所以这就是本篇的背景 - -.~

正文

幸运的是ASP.NET Core 给我们提供了强大的中间件模式.

我们完全可以通过定义一个转发中间件的形式来实现代理接口转发,流程如图:

废话不多说,我们来创建我们的中间件:

一.创建检测约定URL的接口与实现

首先定义一个接口IUrlRewriter 用来检测我们的URL是否有对应前缀,如果有,则产生新的URL地址:

这里我们定义接口是为了方便以后更好的更换注入类来实现快速更换检测前缀的规则.

public interface IUrlRewriter
{
    Task<Uri> RewriteUri(HttpContext context);
}

实现这个接口,如下(解释都在注释里了):

public class PrefixRewriter : IUrlRewriter
  {
    private readonly PathString _prefix; //前缀值
    private readonly string _newHost; //转发的地址

    public PrefixRewriter(PathString prefix, string newHost)
    {
      _prefix = prefix;
      _newHost = newHost;
    }

    public Task<Uri> RewriteUri(HttpContext context)
    {
      if (context.Request.Path.StartsWithSegments(_prefix))//判断访问是否含有前缀
      {
        var newUri = context.Request.Path.Value.Remove(0, _prefix.Value.Length) + context.Request.QueryString;
        var targetUri = new Uri(_newHost + newUri);
        return Task.FromResult(targetUri);
      }

      return Task.FromResult((Uri)null);
    }
  }

二.创建代理转发需要的ProxyHttpClient

创建独立的ProxyHttpClient,主要是为了区分代理转发的httpClient,方便后期添加日志或做别的处理.代码如下:

public class ProxyHttpClient
  {
    public HttpClient Client { get; private set; }

    public ProxyHttpClient(HttpClient httpClient)
    {
      Client = httpClient;
    }
  }

三.创建代理转发的中间件

代码如下,中间件嘛,主要就是Invoke方法了,说明可以看注释.

public class ProxyMiddleware
  {
    // private ProxyHttpClient _proxyHttpClient;
    private const string CDN_HEADER_NAME = "Cache-Control";
    private static readonly string[] NotForwardedHttpHeaders = new[] { "Connection", "Host" };
    private readonly RequestDelegate _next;
    private readonly ILogger<ProxyMiddleware> _logger;

    public ProxyMiddleware(
        RequestDelegate next,
        ILogger<ProxyMiddleware> logger
        
        )
    {
      _next = next;
      _logger = logger;
      //_proxyHttpClient = proxyHttpClient;
    }

    /// <summary>
    /// 通过中间件,拦截访问,检测前缀,并转发
    /// </summary>
    /// <param name="context"></param>
    /// <param name="urlRewriter"></param>
    /// <returns></returns>
    public async Task Invoke(HttpContext context, IUrlRewriter urlRewriter, ProxyHttpClient proxyHttpClient)
    {
      var targetUri = await urlRewriter.RewriteUri(context);

      if (targetUri != null)
      {
        var requestMessage = GenerateProxifiedRequest(context, targetUri);
        await SendAsync(context, requestMessage, proxyHttpClient);

        return;
      }

      await _next(context);
    }

    private async Task SendAsync(HttpContext context, HttpRequestMessage requestMessage, ProxyHttpClient proxyHttpClient)
    {
    

      using (var responseMessage = await proxyHttpClient.Client.SendAsync(requestMessage, HttpCompletionOption.ResponseHeadersRead, context.RequestAborted))
      {
        context.Response.StatusCode = (int)responseMessage.StatusCode;

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

        if (!context.Response.Headers.ContainsKey(CDN_HEADER_NAME))
        {
          context.Response.Headers.Add(CDN_HEADER_NAME, "no-cache, no-store");
        }

        await responseMessage.Content.CopyToAsync(context.Response.Body);
      }
    }

    private static HttpRequestMessage GenerateProxifiedRequest(HttpContext context, Uri targetUri)
    {
      var requestMessage = new HttpRequestMessage();
      CopyRequestContentAndHeaders(context, requestMessage);
      requestMessage.RequestUri = targetUri;
      requestMessage.Headers.Host = targetUri.Host;
      requestMessage.Method = GetMethod(context.Request.Method);
      return requestMessage;
    }

    private static void CopyRequestContentAndHeaders(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)
      {
        if (!NotForwardedHttpHeaders.Contains(header.Key))
        {
          if (header.Key != "User-Agent")
          {
            if (!requestMessage.Headers.TryAddWithoutValidation(header.Key, header.Value.ToArray()) && requestMessage.Content != null)
            {
              requestMessage.Content?.Headers.TryAddWithoutValidation(header.Key, header.Value.ToArray());
            }
          }
          else
          {
            string userAgent = header.Value.Count > 0 ? (header.Value[0] + " " + context.TraceIdentifier) : string.Empty;

            if (!requestMessage.Headers.TryAddWithoutValidation(header.Key, userAgent) && requestMessage.Content != null)
            {
              requestMessage.Content?.Headers.TryAddWithoutValidation(header.Key, userAgent);
            }
          }

        }
      }
    }

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

四.注入和启用我们的中间件和ProxyHttpClient

我们在Startup的ConfigureServices中添加如下代码,注入我们的HttpClient与IUrlRewriter,如下:

services.AddHttpClient<ProxyHttpClient>()
      .ConfigurePrimaryHttpMessageHandler(x => new HttpClientHandler()
      {
        AllowAutoRedirect = false,
        MaxConnectionsPerServer = int.MaxValue,
        UseCookies = false,
      }); //注入我们定义的HttpClient
 services.AddSingleton<IUrlRewriter>(new PrefixRewriter("/webapp", "http://localhost:63445"));//这里填写前缀与需要转发的地址

然后在Startup的Configure中,启动我们的中间件,如下:

app.UseMiddleware<ProxyMiddleware>();

五.测试中间件效果

我们编写前端代码如下:

created: function () {
        this.mockTableData1();
        axios.get("/webapp/api/values/get", "123").then(res => { alert(res.data[0]) });
        axios.post("/webapp/api/values/post",{value: 'david'}).then(res => { alert(res.data.message) });

      }

在另外的WebApi项目,编写接口如下:

[HttpGet]
    public ActionResult<IEnumerable<string>> Get()
    {
      return new string[] { "value1", accstring.ToString() };
    }

    [HttpPost]
    public AjaxResult Post(dynamic value)
    {
      string aaa = JsonConvert.SerializeObject(value);
      return Success("OK");
    }

效果如下,可以看到我们的视图正确的获取到了返回值:

写在最后

这里我们通过中间件的形式实现了接口的代理转发,在具体的使用过程中肯定还会有一些小问题,而且这里我们只实现了Http的转发.ws的则没有.

如果要使用的话,其实国外有一个开源的项目:https://github.com/ProxyKit, 已经有900多个star了.应该还不错.

到此这篇关于ASP.NET Core 奇技淫巧之接口代理转发的实现的文章就介绍到这了,更多相关ASP.NET Core 接口代理转发内容请搜索猪先飞以前的文章或继续浏览下面的相关文章希望大家以后多多支持猪先飞!

[!--infotagslink--]

相关文章

  • ASP.NET购物车实现过程详解

    这篇文章主要为大家详细介绍了ASP.NET购物车的实现过程,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-09-22
  • .NET Core下使用Kafka的方法步骤

    这篇文章主要介绍了.NET Core下使用Kafka的方法步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-09-22
  • 在ASP.NET 2.0中操作数据之七十二:调试存储过程

    在开发过程中,使用Visual Studio的断点调试功能可以很方便帮我们调试发现程序存在的错误,同样Visual Studio也支持对SQL Server里面的存储过程进行调试,下面就让我们看看具体的调试方法。...2021-09-22
  • c# 三种方法调用WebService接口

    这篇文章主要介绍了c# 三种方法调用WebService接口的相关资料,文中示例代码非常详细,帮助大家更好的理解和学习,感兴趣的朋友可以了解下...2020-07-07
  • vue接口请求加密实例

    这篇文章主要介绍了vue接口请求加密实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-08-12
  • 详解.NET Core 3.0 里新的JSON API

    这篇文章主要介绍了详解.NET Core 3.0 里新的JSON API,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-09-22
  • ASP.NET Core根据环境变量支持多个 appsettings.json配置文件

    这篇文章主要介绍了ASP.NET Core根据环境变量支持多个 appsettings.json配置文件,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-09-22
  • 记一次EFCore类型转换错误及解决方案

    这篇文章主要介绍了记一次EFCore类型转换错误及解决方案,帮助大家更好的理解和学习使用asp.net core,感兴趣的朋友可以了解下...2021-09-22
  • C#简单了解接口(Interface)使用方法

    这篇文章主要介绍了C#简单了解接口(Interface)使用方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下...2020-12-08
  • SpringBoot接口接收json参数解析

    这篇文章主要介绍了SpringBoot接口接收json参数解析,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-10-19
  • 自动设置安卓手机wifi代理的PowerShell脚本

    这篇文章主要介绍了自动设置安卓手机wifi代理的PowerShell脚本,帮助大家进行抓包测试,感兴趣的朋友可以了解下...2020-10-17
  • C# Rx的主要接口深入理解

    这篇文章主要介绍了C# Rx的主要接口深入理解的相关资料,需要的朋友可以参考下...2020-06-25
  • Feign接口方法返回值设置方式

    这篇文章主要介绍了Feign接口方法返回值设置方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-07-08
  • 如何设计一个安全的API接口详解

    在日常开发中,总会接触到各种接口,前后端数据传输接口,第三方业务平台接口,下面这篇文章主要给大家介绍了关于如何设计一个安全的API接口的相关资料,需要的朋友可以参考下...2021-08-12
  • vue设置全局访问接口API地址操作

    这篇文章主要介绍了vue设置全局访问接口API地址操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-08-14
  • 详解ASP.NET Core 中基于工厂的中间件激活的实现方法

    这篇文章主要介绍了ASP.NET Core 中基于工厂的中间件激活的实现方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-09-22
  • php怎么写api接口?php写api接口的实例代码

    php怎么写api接口?本文介绍了php写api接口的实例代码,有兴趣的同学可以参考一下。 http://localhost/openUser.php?act=get_user_list&type=json在这里openUser.php...2017-07-06
  • vue配置多代理服务接口地址操作

    这篇文章主要介绍了vue配置多代理服务接口地址操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-09-08
  • asp.net通过消息队列处理高并发请求(以抢小米手机为例)

    这篇文章主要介绍了asp.net通过消息队列处理高并发请求(以抢小米手机为例),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-09-22
  • Underscore源码分析

    Underscore 是一个 JavaScript 工具库,它提供了一整套函数式编程的实用功能,但是没有扩展任何 JavaScript 内置对象。这篇文章主要介绍了underscore源码分析相关知识,感兴趣的朋友一起学习吧...2016-01-02