uni.app手写网络拦截器

2,201 阅读3分钟

创建两个类

编写请求拦截和响应拦截的时候就调用use将请求拦截的方法和响应拦截的方法分别存入两个对应的实例中。

实例中的实例属性handlers数组就是用来保存对应的方法的。

class InterceptorManager{
	constructor() {
	   this.handlers=[];
		 this.name="hansu"
	}
	use(resolve,reject){
		this.handlers.push({resolve,reject})
	}
	forEach(fn){
	  this.handlers.forEach((elem)=>{
			fn(elem)
		})
	}
}

class Http{
	
constructor() {
	  this.config={};
    this.interceptors={
			request:new InterceptorManager(),
			response:new InterceptorManager(),
		}
}

initConfig(config){
	  Object.assign(this.config,config)
}
	
}

export default new Http()

创建执行链

执行的顺序大概是:请求前配置一些数据->发起请求->对请求数据做一些调整。很明显这是一条链状的结构,用链式调用再好不过,于是就使用promise进行链式调用。

实现原理也很简单,将一开始创造的两个实例中的handlers分别添加到执行链中的头部和尾部,将要发起请求的promise放在中间。

  • dispatchRequest是当前即将要发送的一个请求,先存入chains中

  • 调用InterceptorManager实例中的forEach方法,向chain的头部和尾部分别插入请求拦截的回调函数和响应拦截的回调函数,因为创建的实例对象不同,所以有所区分

  • 最后循环调用chains即可

具体实现:

class Request {
	get(url, data = {}) {
		let config={
		url,
		data,
		method,
		header:{}
		};
	    function getPromise(config){
			return new Promise((resolve, reject) => {	
					uni.request({
					...config,
					success: (res) => {
						resolve(Object.assign({config},res))
                        //将config和res组成新的对象返回,这样可以拿到配置信息
					}
				})
		})		
		}
		function dispatchRequest(config){
			if(http.config.baseURL){
				config.url=http.config.baseURL + url
			}
            //添加baseURL
			return getPromise(config)
		}
		
		let chain=[dispatchRequest,undefined];
		//创建执行链
		http.interceptors.request.forEach(function(interceptor){
			chain.unshift(interceptor.resolve,interceptor.reject)
            //一个是接受状态的回调,一个是拒绝状态的回调
		})
		//在执行链头部中添加请求拦截时传入的方法
		http.interceptors.response.forEach(function(interceptor){
			chain.push(interceptor.resolve,interceptor.reject)
		})
		//在执行链尾部中添加响应拦截时传入的方法
		let promise=Promise.resolve(config);
        //初始化状态就是 resolved,这里形成了promise调用链,就可以直接promise.then()了
		while(chain.length){
			promise=promise.then(chain.shift(),chain.shift())
            //返回chain中第一个元素的值,并将其删除,因为都是成对出现的resolve和reject回调
		}
		return promise
	}

    post(url, data = {}){
		return this.get(url,data,"POST")
	}
}
export default Request

请求拦截和响应拦截

import http from "@/config/http"
http.initConfig({
	baseURL:"http://192.168.1.14:10009/",
	timeout:10000
})
//请求拦截
http.interceptors.request.use((config)=>{
	// console.log("ready to request")
	config.header['x-Token']="sdada";
	return Promise.resolve(config)
})
//响应拦截
http.interceptors.response.use((res)=>{
	console.log(res.config.method)
	if (res.statusCode == 200) {
		let data=JSON.parse(res.data);
		if((data.length>0||Object.keys(data).length>0)&&data){
		}
		else{
			uni.showToast({
				title:"暂无数据",
				icon:"none"
			})
		}
		return Promise.resolve(data)
	} else {
		return Promise.reject("HTTP:状态码异常!");
	}
})

注意点:

1.请求拦截无非就是在请求之前增加一些配置,那如何修改请求中的配置呢?我可以预先初始化一个config,然后通过初始化一个Promise.resolve(config)进行传递。然后在请求拦截的函数中做修改。

2.请求完成后返回的结果会通过resolve传递到响应拦截的方法中,最后做完操作后,通过return将值返回出来。

3.then通常都会有两个回调函数,一个是接受状态,一个是拒绝的状态,所以如果没有传入拒绝的状态就设置为undefined,这样比较规范,以防万一有的传一个,有的传两个,在调用promise的时候不会出错。

4.返回的结果需要和最初的config进行合并,在响应拦截中可以读取最开始的config

5.config的变化

  • 最开始的值是调用get或者post等方法传入的值

  • 然后发送请求的时候会结合Http类中的config的实例属性,做一些修改

  • 最后请求完毕会将config和res合并一起返回

具体可以参考以下文档

MDN中promise