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

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

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

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

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

  那么问题就随之而来了.

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

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

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

  正文

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

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

  

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

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

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

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

  public interface IUrlRewriter

  {

  Task 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 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 _logger;

  public ProxyMiddleware(

  RequestDelegate next,

  ILogger logger,

  ProxyHttpClient proxyHttpClient

  )

  {

  _next=next;

  _logger=logger;

  _proxyHttpClient=proxyHttpClient;

  }

  ///

  /// 通过中间件,拦截访问,检测前缀,并转发

  ///

  ///

  ///

  ///

  public async Task Invoke(HttpContext context, IUrlRewriter urlRewriter)

  {

  var targetUri=await urlRewriter.RewriteUri(context);

  if (targetUri !=null)

  {

  var requestMessage=GenerateProxifiedRequest(context, targetUri);

  await SendAsync(context, requestMessage);

  return;

  }

  await _next(context);

  }

  private async Task SendAsync(HttpContext context, HttpRequestMessage requestMessage)

  {

  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=targetUri;

  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()

  .ConfigurePrimaryHttpMessageHandler(x=> new HttpClientHandler()

  {

  AllowAutoRedirect=false,

  MaxConnectionsPerServer=int.MaxValue,

  UseCookies=false,

  }); //注入我们定义的HttpClient

  services.AddSingleton(new PrefixRewriter("/webapp", "localhost:63445"));//这里填写前缀与需要转发的地址

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

  app.UseMiddleware();

  五.测试中间件效果

  我们编写前端代码如下:

  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> Get()

  {

  return new string[] { "value1", accstring.ToString() };

  }

  [HttpPost]

  public AjaxResult Post(dynamic value)

  {

  string aaa=JsonConvert.SerializeObject(value);

  return Success("OK");

  }

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

  

  写在最后

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

.NET Core 实例接口代理转发相关推荐

  1. vercel和netlify部署代码并解决接口代理转发的问题(和Nginx功能一样)

    前言 部署过程就不说了,部署完成后是这样子的 然后访问链接,无法访问 解决 依次点击 Settings–>Domains,在输入框中输入你的域名并点击 Add 按钮. 以此域名为例子demo.g ...

  2. PHP_代理转发接口

    原理:用PHP的GET和POSt请求,实现代理访问服务器内网接口. (代理不依赖于Nginx.Apache) 假设: 外网链接:http://127.0.0.2 内网地址:http://127.0.0 ...

  3. 基于Servlet体系的HTTP请求代理转发Spring Boot组件

    背景概述 两个项目组原本都是各自负责两个产品线(产品A.产品B),由于公司业务的发展,目前需要将两个产品合并成一个大产品(功能整合,部分做取舍,最终产出产品C),前后端代码必然也需要整合,包括两个产品 ...

  4. 跨域问题解决方案--Nginx代理转发

    文章目录 问题描述 解决方案 问题描述 有这么一个html页面,具体代码如下: Nginx_CORS.html <!DOCTYPE html> <html lang="en ...

  5. app接口服务器请求为什么会报错307_Vue接口代理和数据Mock,你会了吗

    基于Vue和React脚手架演示各自项目的接口代理和数据Mock. Vue接口代理实现步骤 vue中文官方文档:https://cn.vuejs.org/ vue-cli中文官方文档:https:// ...

  6. 阿里云服务器安装并配置nginx代理转发请求

    阿里云服务器安装并配置nginx代理转发请求 下载nginx安装包,或者上传nginx安装包,此处我用的是Xmanager工具从本地上传了一个nginx安装包. 安装依赖库 yum -y instal ...

  7. Nginx:配置 proxy_pass 代理转发

    在nginx中配置proxy_pass代理转发时: 如果在proxy_pass后面的url加/,表示绝对根路径: 如果没有/,表示相对路径,把匹配的路径部分也给代理走. 1.实例说明: 假设下面四种情 ...

  8. vue 移动端音乐(3) amp;amp;gt;热门歌单推荐部分(webpack-dev-conf.js做后端接口代理+scroll插件)

    1. 首先,获取歌单推荐部分的数据,与获取推荐数据不同,热门歌单数据的接口有host和referer的显示,我们的api请求被拒绝(500错误),必须要修改header,但是前端不能直接修改reque ...

  9. Eginx配置(SSL,令牌登录认证,IP白名单,代理转发)

    一般需要对接银行的接口,都需要用前置机,在前置机上安装银行的客户端(CBS).我们接口调用的实际是前置机上的http服务. 为了数据安全,一般会把前置机的网络和服务器的网络处理成同一个内网,防止数据被 ...

最新文章

  1. redis mysql排行榜实现_redis实现排行榜
  2. 新纳米结构能加快电子设备运行
  3. RDD编程 下(Spark自学四)
  4. go语言中goroutine池
  5. java dh算法_dh密钥交换算法java
  6. 都在抢论文第一作者,如何处理?
  7. asp.net中的窗体身份验证(最简单篇)
  8. C++之vector容器初学(二)——插入和删除
  9. linux高级运维笔试简答题及答案,企业linux初级和高级运维面试常问题目问答总结技巧讲解(2020年录制)...
  10. SAP Spartacus B2B 页面 Disable Confirmation 对话框的显示原理
  11. 网络中典型协议--(DNS,输入url后, 发生的事情. ,ICMP,NAT)
  12. 功率曲线k值_什么叫离心泵的流量——功率曲线?它们之间有什么关系?
  13. php strstr 与 str_replace区别,[PHP]strstr(),substr(),str_replace(),parse_str()
  14. 2017-2018-1 20155234第三周《信息安全系统设计基础》学习总结
  15. HttpMessageConvert
  16. 3D视觉创新方案分享:仓储VSLAM/商品三维重建/静态场景重建/表情识别等多个方向...
  17. Go游戏框架初探《一》
  18. 大学课程 | 《微机原理与接口技术》知识点总结
  19. 铁威马NAS搭建邮件服务器教程
  20. 新蓝海之门,或将由数据信托来打开!

热门文章

  1. 5年 Python 功力,总结了 10 个开发技巧
  2. python数据分析与挖掘 | 挖掘建模
  3. 64位cad commondialog添加_常用软件分享PDF转CAD软件
  4. linux日记的监控与分析,linux下apache日志监控与分析——webalizer与awstat
  5. java 实现set,Java--Set的三个具体实现类
  6. Soft-Actor-Critic-强化学习算法
  7. 2019 年百度之星·程序设计大赛 - 初赛一
  8. failed to find romfile efi-virtio.rom
  9. 请求数据分析 xpath语法 与lxml库
  10. 会话技术(session/cookie)