请求拦截器对象篡改问题:如何复制对象避免修改原控制器对象?
解决请求拦截器修改原控制器对象的问题
你遇到的问题本质是:walk函数直接操作了request.data的原始引用,而这个引用和控制器里的原对象指向同一块内存,所以修改时会“牵连”到原对象。解决的关键是先对request.data做深拷贝,得到一个完全独立的新对象,再对这个拷贝进行转换操作,这样原对象就不会被改动了。
下面给你几个可行的方案,适配你的代码场景:
方案1:用Angular原生的angular.copy(最推荐)
angular.copy是Angular专门做深拷贝的方法,能完美复制嵌套对象、数组,彻底切断和原对象的引用关联。修改你的拦截器代码如下:
app.factory('requestInterceptor', function () { function walk(obj) { _.keys(obj).forEach(key => { if (moment.isMoment(obj[key])) { // 替换成你需要的后端兼容格式,比如: obj[key] = obj[key].format('YYYY-MM-DD HH:mm:ss'); } if (obj[key] === false || obj[key] === true) { obj[key] = obj[key] ? 1 : 0; } if (_.isArray(obj[key]) || _.isObject(obj[key])) { walk(obj[key]); } }); } return { request(request) { if (request.data) { // 深拷贝出独立的新对象 const copiedData = angular.copy(request.data); // 只操作拷贝后的对象 walk(copiedData); // 把处理好的对象赋值给request.data request.data = copiedData; } return request; }, requestError(request) { return request; } }; });
方案2:修复JSON序列化反序列化的用法
你之前试过JSON序列化,但应该是没处理好顺序。这种方法的好处是不需要依赖其他工具,但要注意:JSON序列化会把moment对象自动转成ISO格式的字符串,如果这个格式刚好符合后端要求,那可以简化moment的处理;如果不符合,还是用方案1更稳妥。
调整后的代码:
app.factory('requestInterceptor', function () { function walk(obj) { _.keys(obj).forEach(key => { if (obj[key] === false || obj[key] === true) { obj[key] = obj[key] ? 1 : 0; } if (_.isArray(obj[key]) || _.isObject(obj[key])) { walk(obj[key]); } }); } return { request(request) { if (request.data) { // 序列化+反序列化实现深拷贝,moment会被转成ISO字符串 let copiedData = JSON.parse(JSON.stringify(request.data)); // 只处理布尔值转换 walk(copiedData); request.data = copiedData; } return request; }, requestError(request) { return request; } }; });
方案3:用Lodash的_.cloneDeep
如果你的项目已经引入了Lodash,_.cloneDeep也是非常可靠的深拷贝工具,用法和angular.copy几乎一致:
app.factory('requestInterceptor', function () { function walk(obj) { _.keys(obj).forEach(key => { if (moment.isMoment(obj[key])) { obj[key] = obj[key].format('YYYY-MM-DD HH:mm:ss'); } if (obj[key] === false || obj[key] === true) { obj[key] = obj[key] ? 1 : 0; } if (_.isArray(obj[key]) || _.isObject(obj[key])) { walk(obj[key]); } }); } return { request(request) { if (request.data) { const copiedData = _.cloneDeep(request.data); walk(copiedData); request.data = copiedData; } return request; }, requestError(request) { return request; } }; });
为什么你之前用_.assign没用?
_.assign是浅拷贝,只会复制对象的第一层属性,嵌套的对象或数组仍然是和原对象共享引用的,所以修改嵌套内容时还是会影响原对象,这就是它没解决问题的原因。
内容的提问来源于stack exchange,提问作者Ørnulf Arntsen
相关产品推荐
相关产品推荐

