nodejs--反向代理 http-proxy-middleware
http-proxy-middleware 详细介绍
http-proxy-middleware
是一个用于 Node.js 的 Express/Connect 中间件,用于将 HTTP 请求代理到其他服务器。它是开发中常用的反向代理工具,特别适合前端开发人员在本地开发环境中使用,解决跨域问题或模拟后端 API。
基本概念
什么是 HTTP 代理中间件?
HTTP 代理中间件是一个能够拦截 HTTP 请求并将其转发到其他服务器的工具。它充当客户端和目标服务器之间的中介,常用于:
- 开发环境中的 API 代理
- 负载均衡
- 跨域请求处理
- 请求/响应修改
核心功能
- 请求转发:将特定路径的请求转发到目标服务器
- 路径重写:修改请求路径后再转发
- 请求/响应修改:在代理过程中修改请求头或响应内容
- WebSocket 支持:可以代理 WebSocket 连接
- 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);
性能考虑
- 连接池:默认情况下,
http-proxy-middleware
会为每个请求创建新的TCP连接。对于高并发场景,可以配置agent
选项使用连接池:
const http = require('http');
const keepAliveAgent = new http.Agent({ keepAlive: true });
createProxyMiddleware({
target: 'http://api.example.com',
agent: keepAliveAgent,
});
- 响应缓冲:默认情况下,代理会缓冲整个响应后再发送给客户端。对于大文件,可以禁用缓冲:
createProxyMiddleware({
target: 'http://api.example.com',
buffer: false,
});
安全注意事项
- 不要在生产环境暴露代理:开发工具代理仅用于开发环境
- 限制代理目标:避免开放代理可能带来的安全风险
- 验证输入:如果使用动态路由,确保验证输入
- 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}`);
});