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


推荐阅读
  • RecyclerView初步学习(一)
    RecyclerView初步学习(一)ReCyclerView提供了一种插件式的编程模式,除了提供ViewHolder缓存模式,还可以自定义动画,分割符,布局样式,相比于传统的ListVi ... [详细]
  • 优化ListView性能
    本文深入探讨了如何通过多种技术手段优化ListView的性能,包括视图复用、ViewHolder模式、分批加载数据、图片优化及内存管理等。这些方法能够显著提升应用的响应速度和用户体验。 ... [详细]
  • 本文详细介绍如何使用arm-eabi-gdb调试Android平台上的C/C++程序。通过具体步骤和实用技巧,帮助开发者更高效地进行调试工作。 ... [详细]
  • Explore a common issue encountered when implementing an OAuth 1.0a API, specifically the inability to encode null objects and how to resolve it. ... [详细]
  • 本文详细介绍了如何使用PHP检测AJAX请求,通过分析预定义服务器变量来判断请求是否来自XMLHttpRequest。此方法简单实用,适用于各种Web开发场景。 ... [详细]
  • 本文详细介绍了如何在Linux系统上安装和配置Smokeping,以实现对网络链路质量的实时监控。通过详细的步骤和必要的依赖包安装,确保用户能够顺利完成部署并优化其网络性能监控。 ... [详细]
  • 使用 Azure Service Principal 和 Microsoft Graph API 获取 AAD 用户列表
    本文介绍了一段通用代码示例,该代码不仅能够操作 Azure Active Directory (AAD),还可以通过 Azure Service Principal 的授权访问和管理 Azure 订阅资源。Azure 的架构可以分为两个层级:AAD 和 Subscription。 ... [详细]
  • 深入解析Spring Cloud Ribbon负载均衡机制
    本文详细介绍了Spring Cloud中的Ribbon组件如何实现服务调用的负载均衡。通过分析其工作原理、源码结构及配置方式,帮助读者理解Ribbon在分布式系统中的重要作用。 ... [详细]
  • 本文详细介绍了Java编程语言中的核心概念和常见面试问题,包括集合类、数据结构、线程处理、Java虚拟机(JVM)、HTTP协议以及Git操作等方面的内容。通过深入分析每个主题,帮助读者更好地理解Java的关键特性和最佳实践。 ... [详细]
  • 在当前众多持久层框架中,MyBatis(前身为iBatis)凭借其轻量级、易用性和对SQL的直接支持,成为许多开发者的首选。本文将详细探讨MyBatis的核心概念、设计理念及其优势。 ... [详细]
  • 资源推荐 | TensorFlow官方中文教程助力英语非母语者学习
    来源:机器之心。本文详细介绍了TensorFlow官方提供的中文版教程和指南,帮助开发者更好地理解和应用这一强大的开源机器学习平台。 ... [详细]
  • 使用C#开发SQL Server存储过程的指南
    本文介绍如何利用C#在SQL Server中创建存储过程,涵盖背景、步骤和应用场景,旨在帮助开发者更好地理解和应用这一技术。 ... [详细]
  • 网络运维工程师负责确保企业IT基础设施的稳定运行,保障业务连续性和数据安全。他们需要具备多种技能,包括搭建和维护网络环境、监控系统性能、处理突发事件等。本文将探讨网络运维工程师的职业前景及其平均薪酬水平。 ... [详细]
  • PHP 5.5.0rc1 发布:深入解析 Zend OPcache
    2013年5月9日,PHP官方发布了PHP 5.5.0rc1和PHP 5.4.15正式版,这两个版本均支持64位环境。本文将详细介绍Zend OPcache的功能及其在Windows环境下的配置与测试。 ... [详细]
  • 深入解析:阿里实战 SpringCloud 微服务架构与应用
    本文将详细介绍 SpringCloud 在微服务架构中的应用,涵盖入门、实战和案例分析。通过丰富的代码示例和实际项目经验,帮助读者全面掌握 SpringCloud 的核心技术和最佳实践。 ... [详细]
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社区 版权所有