uniapp请求接口封装

uniapp请求接口封装

uniapp发送请求跟web的不同,而且通过uni.request这个方法进行调用。

示例:

js 复制代码
uni.request({
    url: 'https://www.example.com/request', //仅为示例,并非真实接口地址。
    data: {
        text: 'uni.request'
    },
    header: {
        'custom-header': 'hello' //自定义请求头信息
    },
    success: (res) => {
        console.log(res.data);
        this.text = 'request success';
    }
});

可能对于习惯了使用axios的开发者而言,会不太喜欢使用这种方式。因此文章讲介绍一下怎么在uniapp上实现一个类似axios的请求方式。

定义构造函数

在这里命名为UniAxios

js 复制代码
class UniAxios {
	constructor(options = {}) {
		this.defaults = options;
		this.interceptors = {
			request: new InterceptorManager(),
			response: new InterceptorManager(),
		};
	}
}

UniAxios构造函数里创建了两个拦截器管理实例。

同时添加了拦截器处理

拦截器

拦截器管理机制其实很简单。就只有一个handlers属性(用于保存拦截器)及三个原型方法(添加、移除、执行)

js 复制代码
class InterceptorManager {
	constructor() {
		this.handlers = [];
	}
}

InterceptorManager.prototype.use = function use(fulfilled, rejected, options) {
	this.handlers.push({
		fulfilled: fulfilled,
		rejected: rejected,
		// 默认异步
		synchronous: options ? options.synchronous : false
	});
	// 当前拦截器id
	return this.handlers.length - 1;
};

InterceptorManager.prototype.remove = function remove(id) {
	if (this.handlers[id]) {
		// 只移除拦截器引用,不处理数组,否则会影响之前的拦截器索引
		this.handlers[id] = null;
	}
};

InterceptorManager.prototype.forEach = function forEach(fn) {
	this.handlers.forEach((handler) => {
		if (handler !== null) {
			fn(handler);
		}
	});
};

处理不同平台请求method

对于不同平台,可能支持的method不一致,因此这里也需要进行处理。

因为不同的请求方式在uni.request中只是method字段的不同,完全可以单独把uni.request封装好,通过传递method来实现不同的请求方法。

以微信小程序为例:

js 复制代码
// 微信小程序支持的请求方法
const supportedMethodType = [
	'GET',
	'POST',
	'PUT',
	'DELETE',
	'CONNECT',
	'HEAD',
	'OPTIONS',
	'TRACE',
];


// 通过遍历的方式添加到构造函数的原型上
supportedMethodType.forEach((item) => {
	LeoAxios.prototype[item.toLocaleLowerCase()] = function(url, data, header, ...args) {
		return this.request(
			item.toLocaleLowerCase(),
			url,
			data, {
				...this.defaults.header,
				...header,
			},
			...args
		);
	};
});

上面添加的这些原型方法内部其实都是调用了同一个request方法。

请求发送

request方法中,需要合并上面的不同请求传过来的参数以及初始化UniAxios时的配置。

同时处理拦截器,将拦截器组装成一个数组用于Promise.then的链式调用。

js 复制代码
function processArguments(args) {
	if (args.length > 1) {
		return {
			method: args[0],
			url: args[1],
			data: args[2],
			header: args[3],
			rest: args.length > 4 ? args.slice(3) : undefined,
		};
	}
	return args;
}

function mergeConfig(defaults, config) {
	return Object.assign(defaults, config);
}

LeoAxios.prototype.request = function request() {
	let config = {};
	// 兼容传参
	config = processArguments(arguments);
	// 合并配置
	config = mergeConfig(JSON.parse(JSON.stringify(this.defaults)), config);
	// 请求拦截器栈
	const requestInterceptorChain = [];
	// 拦截器是否是同步的
	let synchronousRequestInterceptors = true;

	this.interceptors.request.forEach(function unshiftRequestInterceptors(
		interceptor
	) {
		synchronousRequestInterceptors =
			synchronousRequestInterceptors && interceptor.synchronous;

		// 将处理方法推进栈中,采用unshift方法
		requestInterceptorChain.unshift(
			interceptor.fulfilled,
			interceptor.rejected
		);
	});

	// 响应拦截器栈
	const responseInterceptorChain = [];

	this.interceptors.response.forEach(function pushResponseInterceptors(
		interceptor
	) {
		responseInterceptorChain.push(interceptor.fulfilled, interceptor.rejected);
	});

	let promise;

	// 异步的处理
	if (!synchronousRequestInterceptors) {
		// 请求方法
		let chain = [dispatchEvent, undefined];
		// 在请求方法前添加请求拦截器
		Array.prototype.unshift.apply(chain, requestInterceptorChain);
		// 在请求方法后添加响应拦截器
		chain = chain.concat(responseInterceptorChain);
		promise = Promise.resolve(config);
		while (chain.length) {
			promise = promise.then(chain.shift(), chain.shift());
		}
		return promise;
	}
	// 请求拦截器
	while (requestInterceptorChain.length) {
		const onFulfilled = requestInterceptorChain.shift();
		const onRejected = requestInterceptorChain.shift();
		try {
			newConfig = onFulfilled(config);
		} catch (err) {
			onRejected(err);
			break;
		}
	}

	// 发送请求
	try {
		promise = dispatchEvent(config);
	} catch (err) {
		return Promise.reject(err);
	}

	while (responseInterceptorChain.length) {
		promise = promise.then(
			responseInterceptorChain.shift(),
			responseInterceptorChain.shift()
		);
	}
	return promise;
};

dispatchEvent方法就是用于发送请求。

js 复制代码
function dispatchEvent(config) {
	let timer,
		requestTask,
		// overtime 请求是否超时
		overtime = false;
	// timer 检测超时定时器,requestTask 网络请求 task 对象,aborted 请求是否已被取消,abort 取消请求方法
	return new Promise((resolve, reject) => {
		if (config.cancel) {
			return reject({
				requestConfig: config,
				errMsg: '网络请求失败:主动取消'
			});
		}
		requestTask = uni.request({
			url: config.url[0] === '/' ? config.baseUrl + config.url : url,
			data: config.data,
			method: config.method,
			header: config.header,
      ...config.rest,
			success: async function success(res) {
				res.requestConfig = config;
				if(config.statusCode && config.statusCode.indexOf(res.statusCode) === -1) {
					reject(res);
				} else {
					resolve(res);
				}
			},
			fail: async function fail(err) {
				if (overtime) {
					return;
				}
				reject({
					...err,
					requestConfig: config
				});
			},
			complete: function complete() {
				// 清除超时定时器
				clearTimeout(timer);
			},
		});

		timer = setTimeout(async () => {
			// 会触发fail方法
			overtime = true;
			requestTask.abort();
			reject({
				requestConfig: config,
				errMsg: '网络请求时间超时'
			});
		}, config.timeout);
	})
}

在这个方法中还添加了超时处理,在超时后调用requestTask.abort方法。

使用

来看看具体怎么使用:

首先初始化一个实例:

js 复制代码
const request = new LeoAxios({
	baseUrl: 'http://localhost:8081/',
	timeout: 60000,
	header: {
		'Content-Type': 'application/json',
	},
	statusCode: [200, 304, 401]
});

传入一些基础配置。

之后就可以使用request.postrequest.get这些方法了。

js 复制代码
request.get('/test', {}, {
	"Content-Type": 'application/x-www-form-urlencoded'
});

request.post('/testpost', {key: 1, value: 2});

拦截器的使用

拦截器有几点需要注意的是:

  • fulfilled方法中需要返回传入的参数,保证下一个拦截器中能获取到传参
  • rejected方法中需要返回一个Promise.reject,这样才能保证在其他拦截器触发的都是rejected
js 复制代码
request.interceptors.request.use(
	function fulfilled(options) {
    return options;
  },
	function rejected(err) {
    return Promise.reject(err);
  },
	option: {
    synchronous: false
  }
);
相关推荐
真的很上进2 小时前
如何借助 Babel+TS+ESLint 构建现代 JS 工程环境?
java·前端·javascript·css·react.js·vue·html
洗发水很好用4 小时前
uniApp打包H5发布到服务器(docker)
uni-app
YUJIAN。5 小时前
使用uniapp开发微信小程序-框架搭建
微信小程序·小程序·uni-app
噢,我明白了6 小时前
同源策略:为什么XMLHttpRequest不能跨域请求资源?
javascript·跨域
sanguine__6 小时前
APIs-day2
javascript·css·css3
关你西红柿子6 小时前
小程序app封装公用顶部筛选区uv-drop-down
前端·javascript·vue.js·小程序·uv
济南小草根6 小时前
把一个Vue项目的页面打包后再另一个项目中使用
前端·javascript·vue.js
小木_.7 小时前
【python 逆向分析某有道翻译】分析有道翻译公开的密文内容,webpack类型,全程扣代码,最后实现接口调用翻译,仅供学习参考
javascript·python·学习·webpack·分享·逆向分析
Aphasia3117 小时前
一次搞懂 JS 对象转换,从此告别类型错误!
javascript·面试
m0_748256567 小时前
Vue - axios的使用
前端·javascript·vue.js