如何修改Chrome扩展中WebSocket的onmessage回调以拦截服务端消息?
问题:Chrome扩展中拦截WebSocket接收消息的实现方案
我正在开发一款Chrome扩展,通过注入脚本替换页面中的WebSocket,仅用于拦截并记录服务端发送的消息。发送消息的拦截可通过重写WebSocket的.send()方法轻松实现,但接收消息的拦截则较为复杂。目前原应用会为socket.onmessage设置自身的回调函数,我已在原型中拦截了这个作为参数传入的函数,但无法修改它,否则会导致功能异常;替换该函数会使其依赖脱离原匿名函数的作用域(WebSocket在该匿名函数中实例化)。请问是否可通过修改传入原型的回调函数来实现此类日志记录?
相关代码
WebSocket包装类核心片段
var WebSocket = function (url, protocols) { 'native'; if (window.location.protocol === 'https:' && url.lastIndexOf('ws:', 0) === 0) { // 防呆处理:HTTPS页面不能使用明文WS连接,直接触发异常 var ws = new Wrapped(url, protocols); if (ws) { ws.close(); } } Object.defineProperties(this, { 'onmessage': { get: function () { return getWrappedProperty(this, 'onmessage', null); }, set: function (value) { // <------ 这里的value就是每次WebSocket收到消息时会被调用的回调函数 setWrappedProperty(this, 'onmessage', value); } },
属性操作工具函数
var getWrappedProperty = function (wrapper, prop, defaultValue) { var wrapped = map.get(wrapper); if (!wrapped) { return defaultValue; } if (wrapped instanceof Wrapped) { return wrapped[prop]; } return wrapped.properties.hasOwnProperty(prop) ? wrapped.properties[prop] : defaultValue; }; /** * 设置指定属性的值 * * @param wrapper 包装实例 * @param prop 属性名 * @param value 要设置的值 */ var setWrappedProperty = function (wrapper, prop, value) { if (value instanceof Function) { value = value.bind(wrapper); } var wrapped = map.get(wrapper); if (!wrapped) { return; } if (wrapped instanceof Wrapped) { wrapped[prop] = value; } else { wrapped.properties[prop] = value; } };
解决方案
完全可以通过包装原回调函数的方式实现日志记录,不需要替换原函数或者修改其作用域。核心思路是在onmessage的setter或setWrappedProperty中,对传入的原回调进行包裹:先执行日志逻辑,再调用原回调,同时完整保留原函数的作用域和绑定关系。
方案一:修改onmessage的setter
直接在onmessage属性的set方法中处理回调包装:
set: function (value) { if (typeof value === 'function') { // 保存原回调,创建包裹函数 const originalCallback = value; value = function(event) { // 插入你的日志记录逻辑 console.log('WebSocket收到消息:', event.data); // 调用原回调,确保this指向和参数完全一致 return originalCallback.call(this, event); }; } setWrappedProperty(this, 'onmessage', value); }
方案二:在setWrappedProperty中统一处理
如果想在工具函数中统一拦截onmessage属性,可以修改setWrappedProperty:
var setWrappedProperty = function (wrapper, prop, value) { // 仅针对onmessage回调做包装 if (prop === 'onmessage' && typeof value === 'function') { const originalCallback = value; value = function(event) { // 执行日志记录 console.log('WebSocket接收消息:', event.data); // 调用原回调,保留上下文 return originalCallback.call(this, event); }; } if (value instanceof Function) { value = value.bind(wrapper); } var wrapped = map.get(wrapper); if (!wrapped) { return; } if (wrapped instanceof Wrapped) { wrapped[prop] = value; } else { wrapped.properties[prop] = value; } };
关键细节说明
- 保留原作用域:通过
originalCallback.call(this, event)确保原回调执行时的this指向不变,不会脱离原匿名函数的作用域。 - 不破坏原功能:原回调的业务逻辑完全保留,只是在其执行前后插入日志代码,不会导致原应用功能异常。
- 兼容绑定关系:工具函数中原有的
value.bind(wrapper)逻辑会自动处理包裹后函数的上下文绑定,确保和原回调的绑定行为一致。
这种方式不需要修改原回调函数本身,而是通过包装层实现无侵入式的日志拦截,完美解决你遇到的问题。
内容的提问来源于stack exchange,提问作者Dude
相关产品推荐
相关产品推荐

