热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

httpproxymiddleware监听并处理返回数据以及禁用缓存httpproxymiddleware使用方法和实现原理(源码解读)

#cnblogs_post_bodyimg{max-width:600px;height:auto}1、参考文档1:https:github.comchimuraihttp-pro

1、参考文档1:https://github.com/chimurai/http-proxy-middleware#compatible-servers

http-proxy-middleware监听并处理返回数据以及禁用缓存
    

http-proxy-middleware使用方法和实现原理(源码解读)

 

2、点开上图链接 https://github.com/http-party/node-http-proxy#listening-for-proxy-events

If you want to handle your own response after receiving the proxyRes, you can do so with selfHandleResponse. As you can see below, if you use this option, you are able to intercept and read the proxyRes but you must also make sure to reply to the res itself otherwise the original client will never receive any data.

Modify response

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);

 

3、参考demo

   通过proxyRes拿到接口的数据后,对数据进行处理

const proxy = require('http-proxy-middleware');
const RouterService = require('../service/RouterService.js')
let target;
if (!process.env.NODE_ENV) { // 本地测试
    target = "http://xx.xx.xxx.xxx:3000"
} else {
    target = "http://172.31.xxx.xxx:3000";
}
var optiOns= {
    target: target,
    selfHandleResponse: true, // handle your own response after receiving the proxyRes
    changeOrigin: true,
    pathRewrite: {
        '^/api/xx/account': '/account',
        '^/api/xx/strategy_name': '/strategy_name',
        '^/api/xx/strategy_key': '/strategy_key',
        '^/api/xx/stats': '/stats',
        '^/api/xx/type': '/type',
        '^/api/xx/positions': '/positions',
        '^/api/xx/klines': '/klines',
        '^/api/xx/orders': '/orders',
    },
    // 需要监听是否正常发送请求以及接收到响应信息可添加
    // onProxyReq: function(proxyReq, req, res){
    //     console.log("request done")
    // },
    onProxyRes: async function (proxyRes, req, res) {
        // 获取接口返回的数据
        let body = {}
        const responseBody = await getBody(proxyRes)
        if (responseBody) body = responseBody

        if (req.url.indexOf('?') > -1) {
            req.url = req.url.substring(0, req.url.indexOf('?'))
        }

        // 对api接口的返回值进行过滤
        let data = body.data
        switch (req.url) {
            case '/type':
                data = await handler(data, 'type', req)
                break
            case '/account':
                data = await handler(data, 'account', req)
                break
            default:
                break
        }
        body.data = data

        // 配置selfHandleResponse : true后,必须通过res响应数据,否则客户端获取不到返回
        // res.end(JSON.stringify(body)) // string或buffer
        res.json(body)
    },
    // onError: function(err, req, res) {
    //     console.log(err)
    // }
};

/**
 * 从proxyRes获取body数据,返回json对象
 * @param {*} proxyRes 
 * @param {*} res 
 */
function getBody(proxyRes) {
    return new Promise((resolve, reject) => {
        let body = []
        proxyRes.on('data', function (chunk) {
            body.push(chunk)
        })
        proxyRes.on('end', function () {
            body = Buffer.concat(body).toString()
            // console.log('getBody ======', body)
            resolve(JSON.parse(body))
        })
    })
}

async function handler(data, apiFilterType, req) {
    // 根据router的pathName查询对应的routerId
    let routerId = 0
    const result = await RouterService.queryRouterByPathName('/api/pnl-v2')
    if (!result.err) routerId = result.id

    // 获取当前用户的角色id
    let roleId = 0
    if (req.session && req.session.user && req.session.user.roles)
        roleId = req.session.user.roles[0].id

    // 根据routerId和roleId查询`router_role_binding`表记录
    let apiResourceFilter = ''
    const result2 = await RouterService.getFilterItemList(roleId, routerId)
    if (!result2.err) apiResourceFilter = result2.apiResourceFilter
    if (apiResourceFilter) apiResourceFilter = JSON.parse(apiResourceFilter)
    // 1718 505 [{account: ['a', 'b' }, {type: ['mm']}]
    // console.log(routerId, roleId, apiResourceFilter)

    return new Promise(resolve => {
        apiResourceFilter.forEach(filterItem => {
            // if (filterItem['type']) {
            if (filterItem[apiFilterType]) {
                // const typeArray = filterItem['type']
                const typeArray = filterItem[apiFilterType]
                // console.log(`typeArray=${JSON.stringify(typeArray)}`)
                data = data.filter(item => {
                    return typeArray.indexOf(item) > -1
                })
            }
        })
        resolve(data)
    })
}

var pnlProxy = proxy(options);
module.exports = pnlProxy;

 

4、代理禁用缓存

  使用过程中发现,连续发两个请求,第二个请求返回代理无法拿到数据

http-proxy-middleware监听并处理返回数据以及禁用缓存
    

http-proxy-middleware使用方法和实现原理(源码解读)

 

   浏览器禁用缓存,再发两个请求,都没有问题

http-proxy-middleware监听并处理返回数据以及禁用缓存
    

http-proxy-middleware使用方法和实现原理(源码解读)

 

   解决方法:客户端的请求头加个 cache-control: no-cache

  proxyReq.setHeader('Cache-Control', 'no-cache');

const proxy = require('http-proxy-middleware')
const RouterService = require('../service/RouterService.js')

// app.js  app.use("/api/pnl-v2", authChecker, pnlProxy)
const baseRouterUrl = '/api/pnl-v2'

let target
if (!process.env.NODE_ENV) { // 本地测试
    target = "http://47.52.xx.xx:3000"
} else {
    target = "http://172.31.xx.xx:3000"
}
const options = {
    target: target,
    selfHandleResponse: true, // handle your own response after receiving the proxyRes
    changeOrigin: true,
    pathRewrite: {
        '^/api/pnl-v2/account': '/account',
        '^/api/pnl-v2/strategy_name': '/strategy_name',
        '^/api/pnl-v2/strategy_key': '/strategy_key',
        '^/api/pnl-v2/stats': '/stats',
        '^/api/pnl-v2/type': '/type',
        '^/api/pnl-v2/positions': '/positions',
        '^/api/pnl-v2/klines': '/klines',
        '^/api/pnl-v2/orders': '/orders',
    },
    // 需要监听是否正常发送请求以及接收到响应信息可添加
    onProxyReq: function(proxyReq, req, res){
        // console.log("request done")
        // 禁用缓存
        proxyReq.setHeader('Cache-Control', 'no-cache');
    },
    onProxyRes: async function (proxyRes, req, res) {
        // 获取接口返回的数据
        let body = {}
        const responseBody = await getBody(proxyRes)
        if (responseBody) body = responseBody

        if (req.url.indexOf('?') > -1) {
            req.url = req.url.substring(1, req.url.indexOf('?'))
        }

        // 对api接口的返回值进行过滤
        body.data = await filterHandler(body.data, req.url, req)
        res.json(body)
    },
    // onError: function(err, req, res) {
    //     console.log(err)
    // }
}

/**
 * 从proxyRes获取body数据,返回json对象
 * @param {*} proxyRes 
 * @param {*} res 
 */
function getBody(proxyRes) {
    let result = {}
    return new Promise(resolve => {
        let body = []
        proxyRes.on('data', function (chunk) {
            body.push(chunk)
        })
        proxyRes.on('end', function () {
            body = Buffer.concat(body).toString()
            try {
                result = JSON.parse(body)
            } catch (err) {
                // 未禁用缓存时,第二次请求body为{}
                console.error('pnlProxy getBody error, body=', body)
            }
            resolve(result)
        })
    })
}

async function filterHandler(data, apiFilterType, req) {
    if (!data) {
        console.error('filterHandler data is empty')
        return
    }

    // 根据router的pathName查询对应的routerId
    let routerId = 0
    const RouterResult = await RouterService.queryRouterByPathName(baseRouterUrl)
    if (!RouterResult.err) routerId = RouterResult.id

    // 获取当前用户的角色id
    let roleIdArray = []
    if (req.session && req.session.user && req.session.user.roles) {
        for (let role of req.session.user.roles) {
            roleIdArray.push(role.id)
        }
    }

    // 根据routerId和roleId查询`router_role_binding`表记录
    let apiResourceFilterArray = [] // [{account: ['xx', 'xx']}, {type: ['mm', 'cta']}]
    for (let roleId of roleIdArray) {
        const resultTemp = await RouterService.getFilterItemList(roleId, routerId)
        if (!resultTemp.err && resultTemp.apiResourceFilter)
            mergeToArray(JSON.parse(resultTemp.apiResourceFilter), apiResourceFilterArray)
    }

    return new Promise(resolve => {
        if (apiResourceFilterArray.length === 0) {
            resolve(data)
            return
        }

        apiResourceFilterArray.forEach(filterItem => {
            if (filterItem[apiFilterType]) {
                const typeArray = filterItem[apiFilterType]
                data = data.filter(item => {
                    return typeArray.indexOf(item) > -1
                })
            }
        })
        resolve(data)
    })

    function mergeToArray(targetArray, sourceArray) {
        const sourceKeys = sourceArray.map(item => Object.keys(item)[0])
        targetArray.forEach(obj => {
            const index = sourceKeys.indexOf(Object.keys(obj)[0])
            if (index > -1) {
                let source = Object.values(sourceArray[index])[0]
                for (let i of Object.values(obj)[0]) {
                    if (source.indexOf(i) === -1) source.push(i)
                }
            } else {
                sourceArray.push(obj)
            }
        })
    }
}

const pnlProxy = proxy(options)
module.exports = pnlProxy

---

http-proxy-middleware的使用可以参考:http-proxy-middleware使用方法和实现原理(源码解读)


推荐阅读
  • http:my.oschina.netleejun2005blog136820刚看到群里又有同学在说HTTP协议下的Get请求参数长度是有大小限制的,最大不能超过XX ... [详细]
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • 在重复造轮子的情况下用ProxyServlet反向代理来减少工作量
    像不少公司内部不同团队都会自己研发自己工具产品,当各个产品逐渐成熟,到达了一定的发展瓶颈,同时每个产品都有着自己的入口,用户 ... [详细]
  • 个人学习使用:谨慎参考1Client类importcom.thoughtworks.gauge.Step;importcom.thoughtworks.gauge.T ... [详细]
  • 模板引擎StringTemplate的使用方法和特点
    本文介绍了模板引擎StringTemplate的使用方法和特点,包括强制Model和View的分离、Lazy-Evaluation、Recursive enable等。同时,还介绍了StringTemplate语法中的属性和普通字符的使用方法,并提供了向模板填充属性的示例代码。 ... [详细]
  • PHP图片截取方法及应用实例
    本文介绍了使用PHP动态切割JPEG图片的方法,并提供了应用实例,包括截取视频图、提取文章内容中的图片地址、裁切图片等问题。详细介绍了相关的PHP函数和参数的使用,以及图片切割的具体步骤。同时,还提供了一些注意事项和优化建议。通过本文的学习,读者可以掌握PHP图片截取的技巧,实现自己的需求。 ... [详细]
  • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
  • 关键词:Golang, Cookie, 跟踪位置, net/http/cookiejar, package main, golang.org/x/net/publicsuffix, io/ioutil, log, net/http, net/http/cookiejar ... [详细]
  • 本文介绍了在Vue项目中如何结合Element UI解决连续上传多张图片及图片编辑的问题。作者强调了在编码前要明确需求和所需要的结果,并详细描述了自己的代码实现过程。 ... [详细]
  • WebSocket与Socket.io的理解
    WebSocketprotocol是HTML5一种新的协议。它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送 ... [详细]
  • 本文介绍了在Linux下安装和配置Kafka的方法,包括安装JDK、下载和解压Kafka、配置Kafka的参数,以及配置Kafka的日志目录、服务器IP和日志存放路径等。同时还提供了单机配置部署的方法和zookeeper地址和端口的配置。通过实操成功的案例,帮助读者快速完成Kafka的安装和配置。 ... [详细]
  • 解决nginx启动报错epoll_wait() reported that client prematurely closed connection的方法
    本文介绍了解决nginx启动报错epoll_wait() reported that client prematurely closed connection的方法,包括检查location配置是否正确、pass_proxy是否需要加“/”等。同时,还介绍了修改nginx的error.log日志级别为debug,以便查看详细日志信息。 ... [详细]
  • 基于Socket的多个客户端之间的聊天功能实现方法
    本文介绍了基于Socket的多个客户端之间实现聊天功能的方法,包括服务器端的实现和客户端的实现。服务器端通过每个用户的输出流向特定用户发送消息,而客户端通过输入流接收消息。同时,还介绍了相关的实体类和Socket的基本概念。 ... [详细]
  • 图像因存在错误而无法显示 ... [详细]
  • 本文讨论了在VMWARE5.1的虚拟服务器Windows Server 2008R2上安装oracle 10g客户端时出现的问题,并提供了解决方法。错误日志显示了异常访问违例,通过分析日志中的问题帧,找到了解决问题的线索。文章详细介绍了解决方法,帮助读者顺利安装oracle 10g客户端。 ... [详细]
author-avatar
Apollo宫保鸡丁
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有