手写前端工程化开发模式下的http反向代理。

60 阅读3分钟

我们前端开发常用的脚手架 react,vue都可以在脚手架下面去配置proxy 解决开发环境下的跨域问题,本质上是webpack or vite 帮我们开了一个代理服务器,今天我就来手写这个代理服务器。

反向代理(Reverse Proxy)是一种网络通信模式,它充当服务器和客户端之间的中介,将客户端的请求转发到一个或多个后端服务器,并将后端服务器的响应返回给客户端。

  1. 负载均衡:反向代理可以根据预先定义的算法将请求分发到多个后端服务器,以实现负载均衡。这样可以避免某个后端服务器过载,提高整体性能和可用性。
  2. 高可用性:通过反向代理,可以将请求转发到多个后端服务器,以提供冗余和故障转移。如果一个后端服务器出现故障,代理服务器可以将请求转发到其他可用的服务器,从而实现高可用性。
  3. 缓存和性能优化:反向代理可以缓存静态资源或经常访问的动态内容,以减轻后端服务器的负载并提高响应速度。它还可以通过压缩、合并和优化资源等技术来优化网络性能。
  4. 安全性:反向代理可以作为防火墙,保护后端服务器免受恶意请求和攻击。它可以过滤恶意请求、检测和阻止攻击,并提供安全认证和访问控制。
  5. 域名和路径重写:反向代理可以根据特定的规则重写请求的域名和路径,以实现 URL 路由和重定向。这对于系统架构的灵活性和可维护性非常有用。

首先我们先编写一个简单的html页面,在这个页面下发送一个网络请求

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    DocumentDocumentDocumentDocument
  </body>
</html>

然后编写一个http服务器,这个服务器跑在80端口,并返回上面的html代码

// 使用http-server-middleware
const http = require("http");
const url = require("url");
const fs = require("fs");
const html = fs.readFileSync("./02index.html");
 
http
  .createServer((req, res) => {
    const { pathname } = url.parse(req.url);
    //    拦截到 所有的http请求
 
    res.writeHead(200, {
      "Content-Type": "text/html",
    });
    res.end(html);
  })
  .listen(80, () => {
    console.log("服务启动成功~");
  });

这个时候 我们可以看到web页面

image.png

此时我们在html页面上 加上网络请求。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    DocumentDocumentDocumentDocument

    <script>
      fetch("/api").then((res) => res.text());
    </script>
  </body>
</html>

在80端口上面开的服务器做更改,配置一下需要反向代理的逻辑,安装http-proxy-middleware 这里的逻辑主要是 进行代理配置的引入,对请求url的拦截,如果需要做代理,就使createProxyMiddleware去做处理,进行代理转发 在这个例子中,如果是/api开头的请求,都发往localhost:3000

npm install http-proxy-middleware

localhost3000上面启动一个简单的服务器

const http = require("http");
const url = require("url");

http
  .createServer((req, res) => {
    const { pathname } = url.parse(req.url);
    if (pathname === "/api") {
      res.end("proxy success");
    }
  })
  .listen(3000, () => {
    console.log("3000 port started ~ ");
  });

代理转发规则如下: proxy.config.js

module.exports = {
  serve: {
    proxy: {
      "/api": {
        target: "http://localhost:3000",
        changeOrigin: true,
      },
    },
  },
};

// 使用http-server-middleware
const http = require("http");
const url = require("url");
const fs = require("fs");
const { createProxyMiddleware } = require("http-proxy-middleware");
const html = fs.readFileSync("./02index.html");
const config = require("./proxy.config");
http
  .createServer((req, res) => {
    const { pathname } = url.parse(req.url);
    //    拦截到 所有的http请求
    // /
    // /favicon.ico

    const proxyList = Object.keys(config.serve.proxy);
    // console.log(proxyList);
    // console.log(pathname);
    if (proxyList.includes(pathname)) {
      const proxy = createProxyMiddleware(config.serve.proxy[pathname]);
      proxy(req, res);
      return;
    }
    res.writeHead(200, {
      "Content-Type": "text/html",
    });
    res.end(html);
  })
  .listen(80, () => {
    console.log("服务启动成功~");
  });

运行的效果如下: 这样就从80代理到了3000端口 并且无跨域

image.png

image.png