使用NodeJs做代理服务研究
利用nodejs做请求的转发
当一个请求发给我们的nodejs服务时,我们想将其转发给其他服务器,并将结果返回给客户端,可以写如下代码:
const axios = require('axios');
const express = require('express');
const app = express();
const targetUrl = 'http://your-target-url.com';
// 创建代理路由
app.all('/proxy', async (req, res) => {
try {
// 发送请求到目标服务器
const response = await axios({
method: req.method,
url: targetUrl + req.originalUrl,
headers: req.headers,
data: req.body
});
// 将目标服务器的响应转发到客户端
res.status(response.status).send(response.data);
} catch (error) {
// 处理请求错误
res.status(500).send('Proxy Error');
}
});
// 监听代理服务器的端口
const port = 3000;
app.listen(port, function () {
console.log(`Proxy server is listening on port ${port}`);
});
基于http-proxy写一个基础的代理服务
同样的,我们也可以使用http-proxy来做代理,其仓库有13.5k的star,我们的webpack-dev-server使用的额http-proxy-middleware,也是基于这个库进行的封装,可以说是node写代理服务的标准方案,如下是基础代码。
// 安装 http-proxy 库
npm install http-proxy
const http = require('http');
const httpProxy = require('http-proxy');
// 创建一个代理服务器实例,并配置目标服务器的地址
const proxy = httpProxy.createProxyServer({
target: 'http://example.com',
});
// 创建一个 HTTP 服务器,并在请求到达时触发代理服务器的转发
const server = http.createServer((req, res) => {
proxy.web(req, res);
});
const port = 3000;
server.listen(port, () => {
console.log(`Proxy server is running on port ${port}`);
});
这样写我们启动服务后,访问http://localhost:3000/
会得到404,寻找原因是因为在代理配置中没有添加changeOrigin:true
const proxy = httpProxy.createProxyServer({
target: 'http://example.com',
changeOrigin:true // 添加此配置就可以成功代理
});
// 通过监听proxyReq事件,我们可以打印转发的请求
proxy.on('proxyReq', function(proxyReq, req, res, options) {
console.log('Proxy Request:', req.method, req.url);
console.log('Proxy Request Headers:', req.headers); // 从中我们可以看到请求头中的host是http://localhost:3000/,如果不设置changeOrigin:true将导致代理网址拒绝我们的请求
});
如何动态更改代理目标
在进行转发时调用proxy.web方法,传递第三个参数,可以重写定位代理目标
const proxy = httpProxy.createProxyServer({
target: 'http://example.com', // 默认代理目标
});
// 创建服务器
const server = http.createServer(function (req, res) {
// 更改代理目标
const target = 条件 ? 'http://another-example.com' : 'http://other-example.com'
proxy.web(req, res, { target });
});
如何重写请求header
在使用代理的过程中,我们同样会遇到更改请求头的需求。上面我们监听过proxyReq事件,在这个事件中,你可以更改响应头。
// 通过监听proxyReq事件,我们可以重写请求header,http-proxy会将修改后的请求发给目标服务器
proxy.on('proxyReq', function(proxyReq, req, res, options) {
proxyReq.setHeader('X-Special-Proxy-Header', 'foobar'); // 添加特殊响应头
proxyReq.setHeader('Cookie', req.headers.cookie); // 将请求的cookie转发给目标,当然我们不写默认也是这么转发的
proxyReq.setHeader('Origin', 目标服务器地址); // 这里req.headers.Origin 是我们代理服务器的地址,可能会导致目标服务器不认可我们的请求,导致不能正确响应,可以改成目标服务器地址
proxyReq.setHeader('Referer', 目标服务器地址); // 同上
});
如何修改响应
除了proxyReq事件,另一个关键事件是proxyRes,通过监听它可以修改返回值。
// 监听proxyRes事件,继续监听其参数proxyRes对象的data和end对象,可以获取到目标服务器返回的响应数据,我们就可以进行更改再返回给客户端
var option = {
target: target,
selfHandleResponse : true // 重点,不写这个配置,下面的修改不会生效。
};
proxy.on('proxyRes', function (proxyRes, req, res) {
var body = [];
proxyRes.on('data', function (chunk) {
body.push(chunk);
});
proxyRes.on('end', function () {
body = Buffer.concat(body).toString();
console.log("res from proxied server:", body);
res.end("my response to cli");
});
});
proxy.web(req, res, option);
基于http-proxy-middleware写一个基础的代理服务
在express框架中使用http-proxy-middleware
实际开发中,一般不会直接使用http-proxy,而是会使用http-proxy-middleware,以中间件的方式融入我们的框架,这种方式更加契合。http-proxy-middleware内部仍然使用的http-proxy,只是做了一层封装。
注意:默认会安装http-proxy-middleware 2.x.x版本,但是官网上的文档是针对3.0.0-beta.1的,2和3版本的api差异较大,为了使用更先进的功能,还是要安装3.0.0-beta.1版本。
const express = require('express');
const { createProxyMiddleware } = require('http-proxy-middleware');
const app = express();
// 创建代理中间件
const proxyMiddleware = createProxyMiddleware({
target: 'http://example.com', // 设置代理目标地址
changeOrigin: true, // 修改请求头中的 Host 字段为目标地址
// 其他可选配置项...
});
// 将代理中间件应用到所有路由
app.use('/', proxyMiddleware);
// 启动 Express 服务器
app.listen(3000, () => {
console.log('Proxy server listening on port 3000');
});
在Koa框架中使用http-proxy-middleware
const Koa = require('koa')
const { createProxyMiddleware } = require('http-proxy-middleware')
const koaConnect = require('koa2-connect')
const app = new Koa()
// 创建代理中间件
const proxyMiddleware = createProxyMiddleware({
target: 'http://example.com', // 替换为目标网址
changeOrigin: true // 设置为true以更改请求的原始主机头
})
// 使用koa2-connect将代理中间件转换为Koa中间件
const proxyHandler = async (ctx, next) => {
// console.log('打印')
await koaConnect(proxyMiddleware)(ctx, next)
}
// 应用代理中间件
app.use(proxyHandler)
app.listen(3000, () => {
console.log('Server is running on port 3000')
})
这里值得注意的点是http-proxy-middleware中间件不能直接给Koa使用,因为Express和Koa中间件的入参不相同,需要使用koa2-connect帮忙转换一下。
使用express还是Koa关系不大,想写好代理服务,重点还是对于http-proxy-middleware的了解和使用。
http-proxy和http-proxy-middleware有什么区别
http-proxy-middleware是基于http-proxy为Express封装的中间件,简化了代理配置的过程,并提供了一些方便的方法和选项来处理常见的代理需求。
const proxyMiddleware = createProxyMiddleware({
// 比如重写请求路径的规则。可以是一个简单的对象,将匹配的路径部分替换为指定的值。看这个配置有没有一点熟悉呢,我们常用的webpack-dev-server也会使用这个配置。
pathRewrite: {
'^/api': '' // 将所有以 '/api' 开头的路径替换为空字符串
}
// 使用pathFilter可以轻松过滤哪些请求需要使用代理服务,哪些路由不使用
pathFilter: function(path, req) {
return path.startsWith('/remote/debug/');
},
// pathFilter也可以配置成一个数组
// pathFilter: ['/api/**', '/ajax/**']
})
如何动态更改代理目标
const proxyMiddleware = createProxyMiddleware({
// 通过router方法可以重定向代理目标,这个配置也只在http-proxy-middleware中有,http-proxy实现同样功能要通过proxy.web方法的第三个配置参数实现。
router: function(req) {
const target = 条件 ? 'http://another-example.com' : 'http://other-example.com'
return target;
}
// router也可以配置成一个对象
router: {
'/api1': 'http://server1.com',
'/api2': 'http://server2.com',
'/api3': 'http://server3.com',
'staging.localhost:3000' : 'http://127.0.0.1:8002', // host only
'localhost:3000/api' : 'http://127.0.0.1:8003', // host + path
},
})
如何重写请求header
在http-proxy-middleware 3.0.0-beta中,将http-proxy原有的事件放在了on对象下:
onst proxyMiddleware = createProxyMiddleware({
target: 'http://example.com', // 替换为目标网址
changeOrigin: true, // 设置为true。heander中host更改的原始主机头
on: {
// 原http-proxy的proxyReq事件
proxyReq: (proxyReq, req, res) => {
},
// 原http-proxy的proxyRes事件
proxyRes: (proxyRes, req, res) => {
},
}
})
所以我们想更改请求头,在proxyReq方法中写跟http-proxy一样的代码即可
on: {
proxyReq: (proxyReq, req, res) => {
proxyReq.setHeader('Origin', 目标服务器地址); // 这里req.headers.Origin 是我们代理服务器的地址,可能会导致目标服务器不认可我们的请求,导致不能正确响应,可以改成目标服务器地址
proxyReq.setHeader('Referer', 目标服务器地址); // 同上
},
}
如何修改响应
http-proxy-middleware修改响应跟在http-proxy的写法不同,使用http-proxy的写法是不生效的。要使用responseInterceptor方法进行响应数据拦截和修改:
on: {
proxyRes: responseInterceptor(async (responseBuffer, proxyRes, req, res) => {
const response = responseBuffer.toString('utf8'); // convert buffer to string
return response.replace('Hello', 'Goodbye'); // manipulate response and return the result
}),
},
如果你只希望修改某些特定请求的响应值,其他请求仍然使用目标服务给的响应,可以这么写:
selfHandleResponse : true, // 一定要写,不写修改的响应不会生效
on: {
proxyRes: responseInterceptor(async (responseBuffer, proxyRes, req, res) => {
// 如果请求的是html页面,则进行更改, 否则返回原响应
if (proxyRes.headers['content-type'] && proxyRes.headers['content-type'].includes('text/html')) {
const response = responseBuffer.toString('utf8'); // convert buffer to string
return response.replace('Hello', 'Goodbye'); // manipulate response and return
}
return responseBuffer;
}),
},
总结
这篇文章初步认识了一下如何使用Nodejs做代理服务。如果相对Nodejs做代理有更多的了解,可以查看http-proxy和http-proxy-middlewire仓库上的更多内容。
转载自:https://juejin.cn/post/7294619778977644585