热门标签 | 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使用方法和实现原理(源码解读)


推荐阅读
  • 本文详细介绍了在Windows系统中如何配置Nginx以实现高效的缓存加速功能,包括关键的配置文件设置和示例代码。 ... [详细]
  • 本文探讨了如何在PHP与MySQL环境中实现高效的分页查询,包括基本的分页实现、性能优化技巧以及高级的分页策略。 ... [详细]
  • Web动态服务器Python基本实现
    Web动态服务器Python基本实现 ... [详细]
  • 本文探讨了如何利用RxJS库在AngularJS应用中实现对用户单击和拖动操作的精确区分,特别是在调整区域大小的场景下。 ... [详细]
  • H5技术实现经典游戏《贪吃蛇》
    本文将分享一个使用HTML5技术实现的经典小游戏——《贪吃蛇》。通过H5技术,我们将探讨如何构建这款游戏的两种主要玩法:积分闯关和无尽模式。 ... [详细]
  • 本文详细介绍了在 CentOS 系统中如何创建和管理 SWAP 分区,包括临时创建交换文件、永久性增加交换空间的方法,以及如何手动释放内存缓存。 ... [详细]
  • 本文介绍了SIP(Session Initiation Protocol,会话发起协议)的基本概念、功能、消息格式及其实现机制。SIP是一种在IP网络上用于建立、管理和终止多媒体通信会话的应用层协议。 ... [详细]
  • 本文详细介绍了如何正确设置Shadowsocks公共代理,包括调整超时设置、检查系统限制、防止滥用及遵守DMCA法规等关键步骤。 ... [详细]
  • 理解浏览器历史记录(2)hashchange、pushState
    阅读目录1.hashchange2.pushState本文也是一篇基础文章。继上文之后,本打算去研究pushState,偶然在一些信息中发现了锚点变 ... [详细]
  • 本文详细介绍了如何利用 Bootstrap Table 实现数据展示与操作,包括数据加载、表格配置及前后端交互等关键步骤。 ... [详细]
  • 实践指南:使用Express、Create React App与MongoDB搭建React开发环境
    本文详细介绍了如何利用Express、Create React App和MongoDB构建一个高效的React应用开发环境,旨在为开发者提供一套完整的解决方案,包括环境搭建、数据模拟及前后端交互。 ... [详细]
  • PHP面试题精选及答案解析
    本文精选了新浪PHP笔试题及最新的PHP面试题,并提供了详细的答案解析,帮助求职者更好地准备PHP相关的面试。 ... [详细]
  • 在Android应用开发过程中,开发者经常遇到诸如CPU使用率过高、内存泄漏等问题。本文将介绍几种常用的命令及其应用场景,帮助开发者有效定位并解决问题。 ... [详细]
  • MySQL InnoDB 存储引擎索引机制详解
    本文深入探讨了MySQL InnoDB存储引擎中的索引技术,包括索引的基本概念、数据结构与算法、B+树的特性及其在数据库中的应用,以及索引优化策略。 ... [详细]
  • 回顾两年前春节期间的一个个人项目,该项目原本计划参加竞赛,但最终作为练习项目完成。独自完成了从编码到UI设计的全部工作,尽管代码量不大,但仍有一定的参考价值。本文将详细介绍该项目的背景、功能及技术实现。 ... [详细]
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社区 版权所有