ajax调用接口数据错乱的原因及解决方法

        前段时间在做项目的时候遇到一个问题,在开发webapp的时候使用ajax调用后端接口的时候,出现了接口数据错乱的问题,现在总结一下

问题描述:

        同一个页面同时调用多个接口:A、B、C、D、E,正常返回结果应该是A-a, B-b, C-c,D-d,E-e。在网络环境比较好的时候,没有问题,然而,在网络环境比较差的时候,错误出现了。返回结果中至少有两个接口返回的结果被其他接口的数据覆盖,结果变成了A-a, B-c, C-c,D-e,E-e,或者A-b, B-b, C-b,D-d,E-e ...... 结果被覆盖的情况完全随机,每次都可能不一样,相同的是每次都会错乱。

        当时第一反应是前端ajax并发导致数据错乱,因为之前自己写PC端和移动端网页的时候(对,不称职的全栈就是我)调用的接口是一样的,没有出现过类似的问题,而现在的webapp是由新入职的前端小哥(不是小白,是资深前端)来做的,新人来了,出现新的问题,那一定是新人这边的问题,所以果断甩锅过去。

        结果前端小哥表示自己也是首次遇到同样的问题,找了很多资料也百思不得其解,我也认为这肯定是前端的问题就没有细查,这个问题就一直悬而未决,直到我忙完了其他事情,前端小哥还没有找出问题,没法海得我自己来了。

开始定位问题

        问题在前端,那就从前端开始,写一个测试页面,请求路径为我们的测试服:


查看控制台,结果:错乱,和之前的错误预估一样,网络环境差的时候,数据错乱

没有对比就没有发言权,所以这次把请求路径变为自己的开发环境:


查看控制台,结果竟然还是错乱,这个问题首次在本地重现了,ok,至少进了一步。

再看看是不是偶然事件,于是多刷新几遍,结果无一例外的全部是数据错乱

之前直接甩锅到前端,因为前端用的框架和我之前用的框架不一样,为了确定是不是框架的原因,我特意直接用jQuery写ajax调用接口,看来框架的原因可以排除了

排除了前端的问题,那就是后端接口的问题了,然后我开始查接口的实现方法,突然发现一个可能的问题,后端使用SpringMVC,所有接口都继承自同一个Controller

......
......
/**
 * 通用工具类
 * @author heh
 */
@SuppressWarnings({ "unchecked", "rawtypes" })
public class BaseController {
	
	......
	......
	
	/**
	 * 提示(用于返回操作结果)
	 */
	protected Tip tip = new Tip();

	......
	......	
}

然后所有的接口都统一使用这个tip作为数据容器然后返回数据,是这样写的

......

@Controller
@SuppressWarnings({ "rawtypes", "unchecked", "unused" })
public class WSControllerImpl extends BaseController implements ...... {

	......

	@Override
	public Tip getUserMessage(HttpServletRequest req) {
		String messageid = req.getParameter("id");
		tip.success(userService.getMessageById(messageid));
		return tip;
	}
	
	......

}

之前为了减少重复代码,就把这个tip容器放到了BaseController里,供其子类调用,没有考虑到不同接口调用这个公共参数的时候,不是独占调用,而是所有的接口都可以修改,所以多个接口同时调用的时候,就会出现A接口调用完毕,还没有返回的时候,tip已经被B接口修改了,所以造成A接口返回的数据和B接口返回的数据变成一样的情况。至此确定了问题。

问题确定之后,解决就简单了,只要让每个接口的tip单独new出来即可,不使用公共资源

回顾确定问题的过程,发现多接口并发执行的时候,少于4个接口的时候不会错乱,大于或等于4个接口的时候,问题必然出现。这也算是一个低级错误了。


总结:在多个子类访问同一个父类的资源的时候,数据类资源最好是不可更改的类型,不要让多个子类同时修改父类的内容。父类的公用内容尽量使用工具操作类的代码,不要用数据共享的内容。代码还是要踏踏实实写,偷懒很容易出问题。


  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
好的,以下是一个基于axios封装的Ajax调用数据接口的示例: ```javascript import axios from 'axios'; const request = axios.create({ baseURL: 'https://api.example.com', // 接口的基础路径 timeout: 5000, // 请求超时时间 }); // 请求拦截器 request.interceptors.request.use(config => { // 在发送请求之前做些什么,比如添加token config.headers.Authorization = 'Bearer ' + getToken(); return config; }, error => { // 对请求错误做些什么 console.error('请求拦截器错误:', error); return Promise.reject(error); }); // 响应拦截器 request.interceptors.response.use(response => { // 对响应数据做些什么,比如处理错误状态码 if (response.status !== 200) { console.error('响应状态码错误:', response.status); return Promise.reject(new Error('响应状态码错误')); } return response.data; }, error => { // 对响应错误做些什么 console.error('响应拦截器错误:', error); return Promise.reject(error); }); export default request; // 调用接口 import request from '@/utils/request'; request({ method: 'get', url: '/api/user', params: { id: 1 } }).then(response => { console.log(response); }).catch(error => { console.error(error); }); ``` 以上代码中,我们通过axios.create()方法创建了一个axios实例,然后定义了请求拦截器和响应拦截器,在发送请求之前可以在请求头中添加token,对响应数据也可以进行一些处理。最后我们导出了这个axios实例,并在调用接口时使用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值