web服务代理用它,还不够吗?

184 阅读2分钟

前言:现大部分项目都是前后端分离,与后端调试时需要使用其提供的服务接口,但是同时开发几个功能对应不同的接口人,这时候就需要频繁的切换接口IP,大部分项目切换IP时需要关闭项目重新启动,非常麻烦。使用node写一个代理就尤为重要,还能自定义返回内容以方便调试。

依赖安装

库介绍:

代理:http-proxy-middleware,使用比较广泛,配置也比较简单

请求拦截:express、body-parser

npm install express@4.18.2 http-proxy-middleware@2.0.6 body-parser@1.20.1 --save -dev

主要代码

    const { createProxyMiddleware, responseInterceptor } = require('http-proxy-middleware');
    const express = require('express');
    const bodyParser = require('body-parser');
    const http = require('http');
    const https = require('https');
    const url = require('url');
    const app = express();
    // 处理application/json内容格式的请求体
    app.use(bodyParser.json());
    // 处理application/x-www-form-urlencoded内容格式的请求体
    app.use(bodyParser.urlencoded({ extended: true }));
    // 选项
    const options = {
        xfwd: false,
        selfHandleResponse: true, //是否修改响应内容
        target: serverInfo.ip, // 目标服务器:代理ip+端口
        changeOrigin: true, // 默认false,是否需要改变原始主机头为目标URL
        ws: true, //代理socket
        secure: false, //是否需要证书认证
        router: function(req) { //动态代理
            //如:http://localhost:3000?proxyIp=http://1.1.1.1将被代理到http://1.1.1.1
            if(req.headers.referer) {
				const hostUrl = new URL(req.headers.referer);
				const proxyIp = hostUrl.searchParams.get('proxyIp');
				if(proxyIp) return proxyIp;
			}
        },
        onProxyReqWs(proxyReq, req, socket) {
            //代理socket
            proxyReq.on('upgrade', function(proxyRes, proxySocket, proxyHead) {
                proxySocket.on('data', (data) => {
                    socket.write(data);
                });
            });
        },
        onError(err, req, res, target) {
            //错误监听
            res?.writeHead?.(500, {
                'Content-Type': 'text/plain',
            });
            res?.end(err.message);
        },
        onProxyReq: async (proxyReq, req, res) => {
            //监听、重写请求头
            const bodyBuffer = Buffer.from(JSON.stringify(req.body));
            proxyReq.setHeader('Content-Length', bodyBuffer.length);
            proxyReq.write(bodyBuffer);
        },
        onProxyRes: (proxyRes, req, res) => {
            //监听、重写返回结果
             return responseInterceptor(async (buffer, proxyRes, req, res) => {
                //响应内容
                let responseTxt = buffer.toString('utf8');
                return buffer;
             })(proxyRes, req, res)
        },
    }
    // 定义 context 函数
    const contextFunction = (contextPath, req) => {
        //不代理的路径
        const localPaths = ['register.json', 'updateDhopPort'];
        const isLocal = localPaths.find((item) => req.url.includes(item));
        if (isLocal && req.method === 'GET') return false;
        return true;
    };
    // 创建反向代理服务
    const proxy = createProxyMiddleware(contextFunction, options);
    // 定义 context 函数
    const server = app.use(proxy);
    // 启动服务
    app.listen(8080, function () {
        console.log([
            '代理服务已启动',
            `设备地址${serverInfo.ip}`,
            `请将proxy、cgi等所有与设备相关的地址修改为:http://localhost:${PORT}`
        ].join(","));
    });
app.get('/otherIp',(req, res) => {
    createProxy({
        targetUrl: `http://localhost:123`,
        errorTips: [
            `访问失败`,
        ],
        req,
        res
    });
});
// 代理中间件函数
function createProxy({ targetUrl, errorTips, req, res }) {
    const target = url.parse(targetUrl);
    const options = {
        hostname: target.hostname,
        port: target.port || (target.protocol === 'https:' ? 443 : 80),
        path: req.originalUrl,
        method: req.method,
        headers: req.headers
    };

    // 移除可能引起问题的头部
    delete options.headers.host;
    delete options.headers.connection;

    const proxyReq = (target.protocol === 'https:' ? https : http).request(options, (proxyRes) => {
        // 设置响应头
        res.writeHead(proxyRes.statusCode, proxyRes.headers);
        proxyRes.pipe(res);
    });

    // 处理错误
    proxyReq.on('error', (err) => {
        console.error('Proxy error:', err);
        res.writeHead(500, { 'Content-Type': 'text/html; charset=utf-8' });
        res.end([
            `失败:${err.message}`,
            `代理信息:${JSON.stringify(options, null, 4)}`,
            ...errorTips,
        ].join(','));
    });
    // 将请求体转发到目标服务器
    req.pipe(proxyReq);
}

各位还有其他方法吗,评论区留言。