nodejs--反向代理 http-proxy-middleware

http-proxy-middleware 详细介绍

http-proxy-middleware 是一个用于 Node.js 的 Express/Connect 中间件,用于将 HTTP 请求代理到其他服务器。它是开发中常用的反向代理工具,特别适合前端开发人员在本地开发环境中使用,解决跨域问题或模拟后端 API。

基本概念

什么是 HTTP 代理中间件?

HTTP 代理中间件是一个能够拦截 HTTP 请求并将其转发到其他服务器的工具。它充当客户端和目标服务器之间的中介,常用于:

  • 开发环境中的 API 代理
  • 负载均衡
  • 跨域请求处理
  • 请求/响应修改

核心功能

  1. 请求转发:将特定路径的请求转发到目标服务器
  2. 路径重写:修改请求路径后再转发
  3. 请求/响应修改:在代理过程中修改请求头或响应内容
  4. WebSocket 支持:可以代理 WebSocket 连接
  5. SSL 绕过:在开发环境中忽略 SSL 证书验证

安装

npm install --save-dev http-proxy-middleware
# 或
yarn add -D http-proxy-middleware

基本使用

在 Express 中使用

const express = require('express');
const { createProxyMiddleware } = require('http-proxy-middleware');

const app = express();

// 代理配置
app.use('/api', createProxyMiddleware({
  target: 'http://api.example.com',
  changeOrigin: true,
  pathRewrite: {
    '^/api': '', // 移除路径中的/api前缀
  },
}));

app.listen(3000);

在 Webpack DevServer 中使用

// webpack.config.js
module.exports = {
  //...
  devServer: {
    proxy: {
      '/api': {
        target: 'http://localhost:3000',
        changeOrigin: true,
      }
    }
  }
};

配置选项

http-proxy-middleware 提供了丰富的配置选项:

选项 类型 默认值 描述
target String - 目标服务器URL
changeOrigin Boolean false 修改请求头中的host为目标URL
pathRewrite Object {} 路径重写规则
router Object/Function - 动态目标路由
logLevel String 'info' 日志级别
onError Function - 错误处理函数
onProxyReq Function - 代理请求前回调
onProxyRes Function - 代理响应后回调
secure Boolean true 是否验证SSL证书
ws Boolean false 是否代理WebSocket

高级用法

路径重写

createProxyMiddleware({
  target: 'http://api.example.com',
  pathRewrite: {
    '^/api/old-path': '/api/new-path', // 重写特定路径
    '^/api': '/rest', // 替换路径前缀
  },
});

动态目标路由

createProxyMiddleware({
  router: function(req) {
    return {
      '/users': 'http://users.api.example.com',
      '/products': 'http://products.api.example.com',
    }[req.path];
  }
});

修改请求头

createProxyMiddleware({
  target: 'http://api.example.com',
  onProxyReq: function(proxyReq, req, res) {
    proxyReq.setHeader('X-Special-Proxy-Header', 'foobar');
  }
});

WebSocket 代理

createProxyMiddleware('/socket.io', {
  target: 'ws://socket.example.com',
  ws: true,
  changeOrigin: true,
});

常见问题解决方案

解决跨域问题

createProxyMiddleware({
  target: 'http://api.example.com',
  changeOrigin: true,
  headers: {
    'Access-Control-Allow-Origin': '*',
    'Access-Control-Allow-Methods': 'GET,POST,PUT,DELETE',
  },
});

处理 HTTPS 证书问题

createProxyMiddleware({
  target: 'https://api.example.com',
  secure: false, // 忽略SSL证书验证
});

代理多个路径

const apiProxy = createProxyMiddleware('/api', {
  target: 'http://api.example.com',
});

const authProxy = createProxyMiddleware('/auth', {
  target: 'http://auth.example.com',
});

app.use(apiProxy);
app.use(authProxy);

性能考虑

  1. 连接池:默认情况下,http-proxy-middleware 会为每个请求创建新的TCP连接。对于高并发场景,可以配置 agent 选项使用连接池:
const http = require('http');
const keepAliveAgent = new http.Agent({ keepAlive: true });

createProxyMiddleware({
  target: 'http://api.example.com',
  agent: keepAliveAgent,
});
  1. 响应缓冲:默认情况下,代理会缓冲整个响应后再发送给客户端。对于大文件,可以禁用缓冲:
createProxyMiddleware({
  target: 'http://api.example.com',
  buffer: false,
});

安全注意事项

  1. 不要在生产环境暴露代理:开发工具代理仅用于开发环境
  2. 限制代理目标:避免开放代理可能带来的安全风险
  3. 验证输入:如果使用动态路由,确保验证输入
  4. HTTPS 配置:生产环境始终使用HTTPS

与类似工具对比

特性 http-proxy-middleware http-proxy nginx
配置复杂度 中等
Node.js 集成 优秀 优秀 需要单独进程
开发便利性 优秀 良好 一般
性能 良好 优秀 优秀
WebSocket 支持
路径重写 有限

V3.0.3版本示例

前端

点击查看代码
<!-- public/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Proxy Example</title>
</head>
<body>
    <h1>Proxy Server Example</h1>
    <button id="getData">Get Data from Backend</button>
    <pre id="response"></pre>

    <script>
        document.getElementById('getData').addEventListener('click', async () => {
            try {
                // 通过代理服务器请求真实后端数据
                const response = await fetch('http://localhost:3000/api/data'); 
                const data = await response.json();
                document.getElementById('response').textContent = JSON.stringify(data, null, 2);
            } catch (error) {
                document.getElementById('response').textContent = 'Error fetching data!';
            }
        });
    </script>
</body>
</html>

代理服务器

点击查看代码
const { createProxyMiddleware } = require("http-proxy-middleware");
const express = require("express");
const app = express();
const cors = require("cors");

app.use(cors());

app.use(
  "/api",
  createProxyMiddleware({
    target: "http://localhost:4000",
    changeOrigin: true,
    on: {
      proxyReq: async (proxyReq, req, res) => {
        console.log("onProxyReq - 请求被代理前");
      },
      proxyRes: async (proxyRes, req, res) => {
        let body = [];
        proxyRes.on("data", (chunk) => body.push(chunk));
        proxyRes.on("end", () => {
          try {
            const data = JSON.parse(Buffer.concat(body).toString());
            res.status(proxyRes.statusCode).json({
              status: "200",
              data,
            });
          } catch (e) {
            res.status(500).json({ error: "Proxy Response Parse Error" });
          }
        });
        // 阻止默认响应
        proxyRes.pipe = () => {};
      },
      error: (err, req, res) => {
        console.log("onError - 发生错误时1");
        res.status(200).json({
          error: "Proxy Error",
          message: err.message,
          details: {
            code: err.code, // 如 ECONNREFUSED
            target: req.path, // 请求路径
          },
        });
      },
    },
    onError: (err, req, res) => {
      console.log("onError - 发生错误时2");
    },
  })
);

app.listen(3000, () => {
  console.log("Server is running on port 3000");
});

后端服务器

点击查看代码
const express = require("express");
const app = express();
const port = 4000;

// 处理 /data 请求,返回一些示例数据
app.get("/data", (req, res) => {
  res.json({
    message: "Hello from the backend!",
    timestamp: new Date().toISOString(),
  });

  // res.status(500).json({
  //     message: 'Internal Server Error',
  //     timestamp: new Date().toISOString()
  // })
});

app.listen(port, () => {
  console.log(`Backend server is running at http://localhost:${port}`);
});

posted @ 2025-04-08 11:40  蜗牛般庄  阅读(749)  评论(0)    收藏  举报
Title
页脚 HTML 代码