如何修改HTTP request对象使其可传递给worker_thread且不破坏关联对象
问题根因
- 直接修改原对象:你实现的
rmObjFunctions是直接修改传入的req对象本身,而Node.js的HTTP请求对象req和响应对象res本身存在互相引用(req.res = res、res.req = req),遍历req时会进入res对象,把res.end等内置方法直接设为null,这就是你看到typeof res.end返回object的原因——typeof null的返回值本身就是object。 - 访问getter触发副作用:你遍历对象属性时直接读取
obj[key],而HTTP请求、Socket对象的很多属性是getter,访问时会执行内部逻辑(比如remoteAddress访问时会调用内部的_getpeername方法),如果这个方法已经被你提前设为null,就会抛出你看到的TypeError。 - 全局的循环引用记录数组:你定义的
objectHistory是模块级全局变量,多次调用rmObjFunctions不会清空这个数组,不仅会内存泄漏,还会导致不同请求的对象处理互相干扰。
修复方案
你需要先深拷贝原始req对象,在拷贝的新对象上做函数清理,绝对不要修改原req、res对象。同时把循环引用记录移到函数内部,遍历时跳过访问器属性,只处理自身可枚举属性,修复后的代码如下:
// common.js function cloneAndRemoveFunctions(obj, history = new WeakMap()) { // 基本类型直接返回 if (obj === null || typeof obj !== 'object') return obj // 处理循环引用 if (history.has(obj)) return history.get(obj) // 处理数组 if (Array.isArray(obj)) { const arrCopy = [] history.set(obj, arrCopy) obj.forEach((item, index) => { arrCopy[index] = cloneAndRemoveFunctions(item, history) }) return arrCopy } // 处理普通对象,只拷贝自身可枚举的非访问器属性 const objCopy = {} history.set(obj, objCopy) const descriptors = Object.getOwnPropertyDescriptors(obj) for (const key in obj) { if (!obj.hasOwnProperty(key)) continue const desc = descriptors[key] // 跳过getter/setter属性,避免触发副作用 if (desc.get || desc.set) continue const val = obj[key] if (typeof val === 'function') { // 需要保留键就设为null,不需要保留直接continue跳过该键即可 objCopy[key] = null } else if (typeof val === 'object') { objCopy[key] = cloneAndRemoveFunctions(val, history) } else { objCopy[key] = val } } return objCopy }
之后将服务端代码中LABEL1处的rmObjFunctions(req)替换为cloneAndRemoveFunctions(req)即可。
注意:如果你的parser需要用到某些getter返回的属性,可以单独加白名单手动获取后赋值到拷贝对象里,比如要用到
remoteAddress,可以在拷贝完成后手动加objCopy.remoteAddress = req.remoteAddress,避免遍历时触发未知副作用。
内容的提问来源于stack exchange,提问作者AvirukBasak
相关产品推荐
相关产品推荐

