前言
公司最近正在做使用uniapp混合开发移动端App,开发过程中,每次写接口,都需要写很多重复性代码。因此想对原生请求方式做个封装,减少写重复性代码。
准备
定义服务器返回值类型
每个家公司后端肯能返回的格式不同,我们公司请求数据后,返回的数据格式是
status
,msg
,data
这三个字段,其中data
我们使用泛型来定义他,因为data类型可能各种各样
export interface IResponse<T = any> {
status: number
msg: string
data: T
}
开始封装
一、拦截器
顾名思义,拦截器的作用就是在发送网络请求前后,对请求头或相应结果等进行一些特殊处理的方法。
uni.addInterceptor('request', OBJECT)
,将拦截 uni.request()
参数名 | 说明 |
---|---|
invoke | 拦截前触发 |
success | 成功回调拦截 |
fail | 失败回调拦截 |
complete | 完成回调拦截 |
whiteApiList
是白名单,不用携带token就可以请求的接口,除白名单外的接口,访问一律跳转到登录页面。
toLoginPage
是对uni.navigateTo
跳转方法的封装。直接跳转登录页面。
getLocal
是对uni.getStorage的封装
// interceptor.ts
// 根地址
export const baseUrl = 'https://test.com'
// 白名单,不需要携带token就允许被访问的接口
const whiteApiList = ['/api/login/mobile', '/api/login', '/api/register/verify', '/api/register/reset']
export const interceptor = () => {
uni.addInterceptor('request', {
// 请求拦截器
invoke(args) {
// 加载loading
uni.showLoading({
title: '加载中...'
})
// 当本地没有token,并且接口地址没在白名单内,一律跳转登录页面
if (!getLocal<string>('token') && !whiteApiList.includes(args.url)) {
toLoginPage()
uni.hideLoading()
return false
}
// request 触发前拼接 url
args.url = baseUrl + args.url
//设置请求头及token
args.header = {
'content-type': args.method === 'POST' ? 'application/json' : 'application/x-www-form-urlencoded',
'Authori-zation': 'Bearer ' + getLocal<string>('token')
}
console.log(args.header)
},
// 响应拦截器,可以对数据进行预处理
success(args) {
uni.hideLoading()
},
fail(err) {
console.log('interceptor-fail', err)
console.log('请求失败')
uni.hideLoading()
},
complete(res) {
uni.hideLoading()
}
})
}
二、get请求
不封装的时候使用get请求
uni.request({
url: url, // 请求地址
method: 'GET', // 请求方式
data, // 发送的数据
success: (res: any) => {
if (res.statusCode === 200) {
console.log(res.data) //请求成功的回调
} else {
console.log(res) // 请求失败的回调
}
}
})
通过上面的例子我们发现,只请求一个接口就需要写十几行代码,何况项目中可能有几十个接口,甚至上百个,如果不封装的话,代码冗余程度难以想象。 封装 通过传入泛型T,我们可以知道,我们想要的数据是什么样的结构.
url
是接口地址,data
是个对象类型的参数。通过Promise获取到成功状态的值。当需要发送get请求是我们直接调用get方法传入参数就好,不用再写重复的代码
export const get = <T>(url: string, data: Record<string, any> = {}) => {
return new Promise<IResponse<T>>((resolve, reject) => {
uni.request({
url: url,
method: 'GET',
data,
success: (res: any) => {
if (res.statusCode === 200) {
resolve(res.data)
} else {
reject(res)
}
}
})
})
}
三、post请求
post封装方式和get封装方式类似,同样传入泛型,通过Promise获取到成功状态的值。
// post请求
export const post = <T>(url: string, data: Record<string, any> = {}) => {
return new Promise<IResponse<T>>((resolve, reject) => {
uni.request({
url: url,
method: 'POST',
data,
success: (res: any) => {
console.log(res)
if (res.statusCode === 200) {
resolve(res.data)
} else {
reject(res)
}
}
})
})
}
使用方法
通过查看接口文档,我们可以先定义接口要返回数据类型。
登录接口:
登录接口使用post请求。一般会返回{token:'xxxxx', msg:'登录成功'}
获取用户信息:
使用get请求,传入用户id。返回{name:'xx', age:18, gender:'男'}
从接口文档我们可以知道,接口将要返回我们什么样的数据,我们可以提前定义这些数据类型。方便我们后续操作。
post用例
//导入post方法
import { post } from "@/xxx"
// 登录接口
const loginUrl = '/api/login'
//服务器要返回的数据类型
interface ILoginResult {
token:string
msg: string
}
// 用户输入的数据
const loginInfo = {username:'xia', password:123}
//传入泛型
post<ILoginResult>(loginUrl,loginInfo).then({data, status} => {
if(status == 200) {
// 数据获取成功
console.log(data.token)
console.log(data.msg)
}
})
get用例
//导入get方法
import { get } from "@/xxx"
// 用户信息接口
const userUrl = '/api/userinfo'
//服务器要返回的数据类型
interface IUserInfo {
name:string
age:18
gender:string
}
// 传入用户id
const ids = {userId:110}
//传入泛型
get<IUserInfo>(userUrl,ids).then({data, status} => {
if(status == 200) {
// 数据获取成功
console.log(data.name) //用户名
console.log(data.age) //年龄
console.log(data.gender) //性别
}
})