充分且简单!使用 Express 打造专属“轻量代理神器”

大厂技术  高级前端  Node进阶

点击上方 程序员成长指北,关注公众号

回复1,加入高级Node交流群

哦你📖阅读本文,你将:

  1. 初步理解什么是 反向代理

  2. 理解为什么2022年,前端离不开 **"反向代理"**。

  3. 跟随作者一步步摸清楚如何开发一个简单而实用的 轻量代理工具

  4. 学会如何把本地代码发布到 npm 全局命令中。

  5. 收货一份少得可怜的源码。

一、什么是反向代理?

度娘说:“反向代理位于用户与目标服务器之间,对用户而言,反向代理相当于目标服务。用户直接访问反向代理服务器就可以获得目标服务器的资源。”

很好理解吧?

还不理解的话,我用更通俗的语法解释下:

你朝反向代理发请求,它将请求转发给其他服务,并将结果返回给你。

70eba0419effdeda1f02563efc515a1b.png

如上图所示,你找代理服务器请求 业务接口/本地资源/对象存储资源,代理服务器分别向不同的资源发出请求,获得资源后,再返还给你。

对于2022年的前端开发而言,反向代理已经是工作中不可或缺的一部分了。

为什么呢?

二、前端为啥需要代理层?

2.1 合理规避“同源策略”

同源策略:浏览器的护城河。

浏览器的 “同源策略” 声明:默认情况下,“跨域” 的请求返回会被拦截下来。

啥是跨域?
当一个请求 url 的协议、域名、端口三者之间任意一个与当前页面 url 不同即为跨域。

例如,你在 https://baidu.com 页面上请求 https://google.com/user/abc 就属于跨域请求。(域名不同)

至于为啥浏览器要有这样一条 “同源策略”,那又是一个可以单独开篇的话题了,本文不进行赘述。

总之,因为 “同源策略” 的存在,前端在开发时就会面临很多的问题,比如:“如何跨域去调用服务端的接口?”

174cc3b94434ef1064a62403adc0d340.png

CORS 是一种解决方案。

CORS (Cross-Origin Resource Sharing),中文名为 “跨域资源共享”,通过服务端在“响应头”、“OPTIONS” 方法上的配合,可以让浏览器允许页面请求跨域资源。

但这其实也会在一定程度上增加安全风险,并需要服务端同学的配合。

“反向代理” 则是一种成本更低,也被更为泛用技术手段。

b87f07ded6148acb172a41c7d75fb5b7.png

如上图所示,注意以下几个关键点:

  1. 当前页面在 http://localhost:8080 域。

  2. 浏览器向 http://localhost:8080 发起请求时,完全符合“同源策略”。

  3. 反向代理将接收到的请求转发给 真实服务器,获得了请求的 数据

  4. 反向代理将得到的 数据 返回给浏览器页面。

  5. 浏览器获得到了它请求的数据。

在整个过程中,既符合浏览器的 “同源策略”, 又不需要服务端介入增加额外的配置,从而不会因为这些增加的配置导致生产环境增加危险系数。

反向代理,让前端研发处理跨域时,随心所欲

2.2 SPA应用:有它更灵活

SPA ( Single Page Application ),中文名为 “单页应用”,是目前市场上前端工程最普遍、最常见的存在形态。

自从 ReactAngularVue 等一批前端框架开始占据市场,SPA 便成为了几乎所有前端都无法绕开的一个关键名词。

SPA 通常情况下,和 webpackvite 等构建工具是相生相伴的好搭档,得益于社区的兴隆,webpack 提供了 devServer 这一特性,能让前端开发者无感地用上了 “反向代理” 的特性。

每当你在控制台输入:

npm run dev
# or
npm run start

的时候,其实前端框架里的 webpack 已经悄悄给你启动了一个 反向代理,你可以在配置文件里轻松地让符合某些规则的请求 被代理 到服务器上。

它不仅提供反向代理,也和 webpack 本身的 hmr (热更新) 绑定在一起,每当资源更新后,你很快就能在页面上看到最新代码所呈现的效果。

23f0dcda49fe34b836ac68f1b143778d.png

这当然是极佳的开发体验,但它也 带来了一个小小的问题

切换服务的成本被提高了

假想一个场景:

当你的服务连着 [环境A] 进行开发时,突然发现这个环境的服务宕机了,于是你熟练地切换到了 [环境B] ,因为你修改了代理配置,你的 webpack 需要重启,对于一些巨石项目或低配电脑,这可能需要5分钟或者更久。

终于,服务起好了,你在 [环境B] 快乐的开发着,此时被告知 [环境A] 修复了,为了联调,你得切回去。好家伙,又是一个 5分钟……

开发过大型 SPA 项目的小伙伴,想必对这种场景并不陌生。

此时,我们就难免会有一种疑问:

反向代理一定要和 开发服务 绑定吗?我切个环境,关您 开发服务 啥事?您重启个什么劲呢?

在这个疑问的驱动下,一个想法出现了:

为什么我们不增加一层 轻量代理层 呢?它只从事代理相关的工作,让 代理构建 彻底解耦呢?

因为专一,所以轻量,从而 切换贼快

90a666196a53e5c05e846dab999684ee.png

2.3 微前端:用它补全资源

随着 SPA 的日渐发展,相关配套逐渐成熟。

但是在某些巨型企业级应用日益庞大臃肿之后,开始暴露出一些让人头疼的问题。

  • 代码结构日渐复杂,难以维护和管理

  • 开发工具启动、热更新效率捉急

  • 模块与模块之间依赖变高,复用成本增加

这类应用,被业内人士起了一个形象的名字——“巨石应用”。

于是有了 “微前端” 的概念,比如 micro.jsqiankun.js,都提供了相应的解决方案。

微前端固然是 大型企业级应用 开发的利器,形成了一种全新的前端开发方式,提供了前端微应用 "制品级" 复用的能力,但它同样引入了新的问题:

开发期可能需要起一堆应用🤣。(如图)

d34a7942a722ea14a8fb1f28c1fa253c.png

当然,不必惊慌,方法总比困难多。

当你需要 “容器” 或 “其他微应用” 的资源时,完全不必局限在本地端口中获取,也可以在某个开发环境上直接拉取。

此时,一个独立轻量的 “代理层” 可能就会显得 恰如其分

9599c33f00694a7874833cb24a508456.png

虽然也可以在每个微应用内设置代理,但是很显然,一个轻量的代理层会让整个结构变得更加便捷,灵活。

比如,此时我想在本地新起一个微应用,想让它进入我的联调范围,相比于重启某个微应的开销,重启代理层的开销就会显得更小。


如上所述:

一个 “轻量代理层” 的存在,对于前端开发而言,真的可以带来非常多的便捷,那么,我们是否可以手写订制一个好用的 “轻量代理层” 呢?

当然可以,现在就写!

跟着春哥一起,手把手带你实现一个 “代理层神器”!

三、手把手!简单实用的自制工具

前端制作开发工具,首选的技术当然是毫无疑问的 nodejs,语言上的熟悉,可以降低非常多不必要的麻烦。

nodejs 生态里,被运用最广的 web 开发框架无疑正是 express,这也是我们接下来要选择使用的框架。

3.1 认识 express

express:
"基于 Node.js,快速、开放、极简的 Web 开发框架。"

可以说,它是 nodejs 生态里,最具影响力的明星框架之一。

通过 express,你可以快速构建一个 web 应用,这个所谓的 web 应用,可以是一个网站、可以是一个接口提供方、可以是一个定时发送邮件的工具,当然,它也可以是我们想要实现的 “轻量代理层”。

简单来说:

你使用 express 可以创建一个应用,该应用可以监听端口,提供 http、https、websocket 等多种形态的网络服务。

关于 express 的一切,如果你有兴趣,可以访问其官网学习:

https://www.expressjs.com.cn/starter/installing.html

express 使用过程中,我们需要理解许多的概念,包括:什么是应用、什么是路由、什么是中间件。

虽然这些在文档中都有官方解释,但为了你能快速理解,我们先写一个简单例子:

mkdir 'lightweight-proxy' # windows 用户请把 mkdir 换成 md
cd lightweight-proxy
yarn init
git init
yarn add express

完成以上步骤,就成功创建了一个项目,并安装了对 express 的依赖。

在项目根目录新建 index.js。

// index.js
const express = require('express')
const app = express() // app 就是一个应用

app.use(function (req, res, next) { // 此方法就是一个最基本的中间件,它在每次请求之前打印时间
  console.log('Time:', Date.now())
  next()
})

app.get('/hello', (req, res) => { // "GET /hello" 就是一个路由
  res.send('Hello World!')
})
app.listen(3000) // 当app监听一个端口时,它就可以被访问了

在浏览器里访问 http://localhost:3000/hello,你可以立刻看到如下效果:

e3ea23480e0a267b3c8cd38591a612e7.png[浏览器端]

并且,控制台上也会出现当次访问的时间戳打印:

640123e8b69cc4ae3c9da39c5b6d0c7b.png

很好,你已经学会了 express 最简单的用法,你又掌握了一项技能,接下来,我们就需要用到一款来自社区的 “中间件” 了。

3.2 利用 http-proxy-middleware 中间件

express 的生态无比繁荣,在 github 上,你可以找到各种场景需要用到的 demo 或者中间件。

http-proxy-middleware 正是这样一个中间件。

它的npm官网:https://www.npmjs.com/package/http-proxy-middleware

官网中的自我介绍:

让 node.js 代理变得容易;作为中间件,它支持 connect, expressbrowser-sync 等诸多框架。

很显然,http-proxy-middleware 是为 “请求代理” 而生的插件,它不仅仅支持 express,还支持更多场景,但本文我们只需要知道它在 epxress 中如何使用即可。

在刚才的工程基础上,先删掉根目录下的 index.js

按以下步骤进行操作:

  1. 执行 yarn add http-proxy-middleware

  2. package.jsonscripts 下新建一个名为 dev 的脚本,内容为:node src/index.mjs

"scripts": {
    "dev": "node src/index.mjs"
  }
  1. 新建 src/index.mjs,并初始化内容:

import express from 'express'
import { createProxyMiddleware } from 'http-proxy-middleware';
const app = express();

// --------- 看这里 ↓ -------------------------
app.use('/', createProxyMiddleware({
  target: 'https://juejin.cn/',
  changeOrigin: true
}));
// --------- 看这里 ↑ -------------------------
app.listen(3000);

以上代码的关键就在注释中着重强调的区域,为路由 '/' 下的所有请求指定使用一个中间件,代理到 'https://juejin.cn/' 地址上去。

因此,当你在浏览器中输入 http://localhost:3000 时,你会惊奇地发现,你打开了掘金的主页:(虽然,因为掘金官网 “防盗链” 机制的存在,你无法刷出文章列表。)

4bffe015daf25a9af8b4296b05f3596b.png

ok,你已经学会了如何使用 http-proxy-middlewareexpress 应用创建一个简单的代理!

是不是很容易?

接下来,我们要结合 实际开发场景 让它接管我们的 请求代理

3.3 在 SPA 应用中实际使用

我先假设,你的项目中有一个 vue.js or react.js 的项目,它占用了 9527 端口。

通常情况下,SPA 应用中都会采用这样的方法区分 “xhr 请求” 和 “前端资源请求”。

  1. 设置一个常量字符串,比如 /prod-api

  2. 在你的所有 xhr 请求中,配置前缀为 /prod-api,比如你想请求 /user/list,那就将其统一定义为 /prod-api/user/list

  3. webpack devServer 中,识别到以 /prod-api 开头,就认定为 xhr 请求,代理到服务端;否则就认定为前端资源请求。

  4. 在所有 /prod-api 请求代理到服务端之前,通过 rewrite 方法去除 /prod-api部分,向服务器发送 http://www.dev-server.com/user/list 请求。(此处可根据服务端情况配置灵活调整)

只要你想明白了以上过程,你也就能清晰地撰写 “轻量代理层” 中间的逻辑了。

  1. /prod-api 开头的请求,将其代理到服务器上。

  2. 其他请求,代理到 http://localhost:9527 上。

修改 src/index.mjs 中的代码为:

import express from 'express'
import { createProxyMiddleware } from 'http-proxy-middleware';
const app = express();

// --------- 看这里 ↓ -------------------------

// 凡是以 '/prod-api' 开头的都认定为 xhr 请求 
app.use('/prod-api', createProxyMiddleware({
  target: 'https://dev.my-server.com/', // 这一行是你的服务端地址
  changeOrigin: true,
  pathRewrite: {
    '^/prod-api': '' // 在向服务端发起请求时,去掉标识xhr的前缀
  }
}));

// 其他请求则认定为前端资源请求,如:html/js/css 等
app.use('/', createProxyMiddleware({
  target: 'http://localhost:9527/',
  changeOrigin: true
}));
// --------- 看这里 ↑ -------------------------

app.listen(3000);

以上配置已经可以顺利运行我手中的几个项目了,你要不要赶紧上手制作一个,看看你手头的项目是否可以顺利运行起来?

当我们想切换接口服务器时,只需要修改 => 停止 => 重启。

切换顺滑如丝,再也不用等待漫长的构建过程了!

3.3 让其在本机全局命令生效

当我们完成上面的步骤后,实际上 “轻量代理层” 就已经可以在日常开发中用起来了,但每次都要找到 lightweight-proxy 的文件夹,运行命令行,属实麻烦,有啥更好的办法吗?

当然!我们把它注册到全局命令中去!

只需要两步:

  1. package.json 文件中定义 bin 属性。

  2. 执行 npm link

3.3.1 增加 bin 属性:

package.json

"bin": {
    "cc-proxy": "./src/index.mjs"
  }

其中 cc-proxy 是我随便乱取的,你也可以根据自己的喜好,将其改为 lol-proxyi-love-study 等各种奇形怪状的名称,都行🤣。

3.3.2 执行 npm link

在项目的根目录下执行如下指令:

npm link
# 不知道为啥,yarn link无法产生正确的指令,所以必须使用npm link

执行完以上两步之后,尝试在任何其他位置打开一个全新的 cmd 或者 powershell 命令行(对于 windows 用户); mac 用户应该也需要打开一个新命令行。

然后执行以下命令:

cc-proxy
4219af5cc69e1d28aada81641fcb2b8a.png

嘿!它生效了!
现在,我们的电脑上正式多了一个专属的 “轻量代理层”!

四、还能做什么?

当然,本文到这里,已经实现了我们初步的目标,那么,我们还可以在这个基础上做些什么呢?

让我们畅想一下:

  1. 支持 https

  2. 将工具打包成 npm 包,发布到私有 npm 源上让同事也能使用?

  3. 提供配置化的能力,在不同的项目通过配置实现不同的效果?

  4. more...

五、源码

细心的春哥,已经把少的可怜的源码上传到了 github,地址:

https://github.com/zhangshichun/lightweight-proxy/tree/main

六、结束语

Node 社群


我组建了一个氛围特别好的 Node.js 社群,里面有很多 Node.js小伙伴,如果你对Node.js学习感兴趣的话(后续有计划也可以),我们可以一起进行Node.js相关的交流、学习、共建。下方加 考拉 好友回复「Node」即可。

如果你觉得这篇内容对你有帮助,我想请你帮我2个小忙:

1. 点个「在看」,让更多人也能看到这篇文章2. 订阅官方博客 www.inode.club 让我们一起成长

点赞和在看就是最大的支持
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值