引言
最近在学习uni,尝试着把之前写的一个小网站(网站地址,博客地址)转换成安卓app。由于我只会web和vue,对uni几乎是零基础,所以在转换的过程中遇见了许多问题。
比如,在原网站里面我的ajax请求是基于axios的,也就是基于XMLHttpRequest的,当我把原网站中ajax模块的代码复制到uni-app中时,发现项目是跑不通的。报了一堆错,我也看不懂,但我猜,uni-app可能不支持XMLHttpRequest,导致axios不能用。
于是,我想着用uni-app提供的uni.request()来做ajax请求,但此方法仅仅具有简单的发送请求和接受响应功能,并不具备axios的拦截器、ts支持等功能。所以,我决定按照axios的使用习惯,对uni.request()进行封装,以实现拦截器和typescript支持。
实现代码
实现代码如下:
UniRequest类就是我们封装的工具类,实现逻辑很简单,直接看代码和注释就可以看懂,所以不再过多描述。
interface UniRespone<T> extends UniApp.RequestSuccessCallbackResult{
data:T
}
interface UniRequestConfig{
baseUrl?:string
header?:Record<string, string>
timeout?:number // 超时限制,毫秒
}
type UniRequestInterceptor = (options: UniApp.RequestOptions) => UniApp.RequestOptions
type UniResponseInterceptor = (response: UniApp.RequestSuccessCallbackResult) => UniApp.RequestSuccessCallbackResult
type UniResponseErrorInterceptor = (err: UniApp.GeneralCallbackResult) => UniApp.GeneralCallbackResult
class UniRequest{
constructor(deaultConfig?:UniRequestConfig){
if(deaultConfig){
this.defaultConfig = Object.assign(this.defaultConfig, deaultConfig)
}
}
// 默认的请求配置
private defaultConfig:UniRequestConfig = {
baseUrl: '',
header: {},
timeout: 30000
}
// 请求拦截器
private requestInterceptors = [] as UniRequestInterceptor[]
// 响应拦截器
private responseInterceptors = [] as UniResponseInterceptor[]
// 响应错误拦截器
private responseErrorInterceptors = [] as UniResponseErrorInterceptor[]
public interceptors = {
request: {
use: (ri:UniRequestInterceptor) => {
this.requestInterceptors.push(ri)
}
},
response: {
use: (ri:UniResponseInterceptor, ei:UniResponseErrorInterceptor) => {
this.responseInterceptors.push(ri)
this.responseErrorInterceptors.push(ei)
}
}
}
// 发起请求,默认配置是defaultConfig,也可以传入config参数覆盖掉默认配置中某些属性
public request<T = any, D = any>(method: 'GET'|'POST', url:string, data?:D, config?:UniRequestConfig):Promise<UniRespone<T>>{
return new Promise((resolve, reject) => {
url = (config?.baseUrl || this.defaultConfig.baseUrl) + url
let header = config?.header ? Object.assign(this.defaultConfig.header, config.header) : this.defaultConfig.header
let timeout = config?.timeout ? config.timeout : this.defaultConfig.timeout
let options:UniApp.RequestOptions = {
method,
url,
header,
timeout,
data,
success: (res) => {
// 执行响应拦截
for(let ri of this.responseInterceptors){
res = ri(res)
}
resolve(res as UniRespone<T>)
},
fail: (err) => {
// 执行响应错误拦截
for(let ei of this.responseErrorInterceptors){
err = ei(err)
}
reject(err)
}
}
// 执行请求拦截器
for(let ri of this.requestInterceptors){
options = ri(options)
}
// 发送请求
uni.request(options)
})
}
// 发起get请求
public get<T = any, D = any>(url:string, params?:D, config?:UniRequestConfig){
return this.request<T, D>('GET', url, params, config)
}
// 发起post请求
public post<T = any, D = any>(url:string, data?:D, config?:UniRequestConfig){
return this.request<T, D>('POST', url, data, config)
}
}
使用方法
UniRequest工具类的使用方法跟axios很类似,我这里用举例子的方式来说明。
假如,目前有一个get请求的接口http://localhost:8000/user,接收一个整数类型的id参数,返回一个User对象,那么代码如下:
// 定义User类型
interface User{
id:number
name:string
age:number
}
// 生成UniRequest实例,类似于axios实例
const uniHttp = new UniRequest({
baseUrl:'http://localhost:8000',
timeout: 10000
})
// 添加请求拦截器,这个写法是不是和axios很像?
uniHttp.interceptors.request.use(options => {
console.log('发起请求:', options.method, options.url)
return options
})
// 添加响应拦截器,这个写法是不是也和axios很像?
uniHttp.interceptors.response.use(
response => {
console.log('接收到响应:', response.statusCode, response.data)
return response
},
err => {
console.log('请求出错啦:', err.errMsg)
return err
}
)
// 发起
uniHttp.get<User, {id:number}>('/user', {id: 1}).then(
res => {
// 这里能自动识别res.data的类型,为User类型
console.log('学生姓名是:', res.data.name)
},
err => {
console.dir('出错啦', err)
}
)
由于例子中的接口并不存在,所以请求当然不能得到正确响应,控制台的输出是这样的:
发起请求: GET http://localhost:8000/user
请求出错啦: request:fail
出错啦 {errMsg: ‘request:fail’}
可以看到拦截器是正确运行的。