You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

请求拦截器对象篡改问题:如何复制对象避免修改原控制器对象?

解决请求拦截器修改原控制器对象的问题

你遇到的问题本质是: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:18:14