1、參考文檔1:https://github.com/chimurai/http-proxy-middleware#compatible-servers
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、代理禁用緩存
使用過程中發現,連續發兩個請求,第二個請求返回代理無法拿到數據
瀏覽器禁用緩存,再發兩個請求,都沒有問題
解決方法:客戶端的請求頭加個 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使用方法和實現原理(源碼解讀)