坑点注意事项
1. 关于 expectDataType
在进行HTTP请求时,我们通常会设置 expectDataType
来指定响应参数的类型。然而,有一个坑需要注意:当使用 expectDataType: http.HttpDataType.OBJECT
来指定响应参数为对象类型时,存在一个BUG。如果结果返回异常,项目会直接瘫痪。为了避免这个问题,我们需要先将接收到的值转换为字符串,然后再解析为对象,例如:JSON.parse(res.result as string)
。在封装通用请求方法时,需要注意这一点。
2. 定义好接收类型
在封装通用请求方法时,需要定义好接收类型为 Promise
对象,并加上泛型参数 <T>
。这样可以使得请求方法返回的结果具有明确的类型,方便后续的处理和类型推断。
3. 处理 get
方法的 extraData
在使用 get
方法进行请求时,需要将 extraData
中的数据拼接到 URL 地址上。这一步需要单独处理,在公共方法里需要注意将extraData
从其中提取出来,并拼接到 URL 地址中。
封装通用请求方法的步骤
-
创建一个网络请求。
-
封装一个通用的请求方法:
-
使用基底址和传输的 URL 拼接为完整 URL 地址。
-
对于
get
方法传输的data
需要拼接在 URL 地址上,而其他方法的data
则直接传入extraData
中。 -
统一设置请求对象
http.HttpRequestOptions
并定义为config
进行处理。
-
-
发起请求(httpRequest.request(urlStr, config)),将完整的 URL 地址和请求对象作为参数传入request方法中发起请求。
-
在处理请求结果时,可以利用返回的状态码
res.responseCode
进行判断处理,例如:401
表示 token 不正确,404
表示请求地址不正确。 -
根据请求的状态码进行进一步的处理。
-
-
在请求完成后,及时销毁请求对象,以释放资源:
httpRequest.destroy()
. -
将封装好的请求方法封装成一个静态的方法类,方便使用者直接调用。可以根据不同的请求方法参数生成四种请求的静态方法,提高代码的可维护性和复用性。
封装的完整代码
import { http } from '@kit.NetworkKit'
// import { TOKEN_KEY } from '../constants'
// import { BASE_URL } from '../constants/url_var'
import { promptAction, router } from '@kit.ArkUI'
// import { ResponsesData } from './request2'
//传输参数token
export const TOKEN_KEY: string = 'token'
//请求网络的基底址
export const BASE_URL: string = 'https://slwl-api.itheima.net/'
// 最外层数据封装类的
export class ResponsesData<T> {
code: number = 0
msg: string = ""
data: T | null = null
}
interface EmptyInterface {}
//Next版本不支持在箭头函数上写纯泛型,
// function requestHttp(url: url地址, method: 请求方法类型,默认为get, data?: 参数类型) : 返回类型是: Promise里面的T类型的数据
async function requestHttp<T>(url: string = '', method: http.RequestMethod = http.RequestMethod.GET, data?: object): Promise<T> {
//创建一个网络请求
const httpRequest = http.createHttp()
//拼接地址
let urlStr = BASE_URL + url
//get方法需要自己拼接
if (method = http.RequestMethod.GET) {
//如果data里面有值并且里面有对象的时候
if (data && Object.keys(data).length) {
urlStr += "?" + Object.keys(data).map(key => {
if (data[key]) {
return `${key}=${data[key]}` // a=1 =>
}
return ""
}).join('&') //['a=1','b=2','c=3']
}
}
//设置请求对象
let config: http.HttpRequestOptions = {
//method同名方法赋值,参数名和属性名相同时只需要写一个method等价于method:method
method,
//超时时间
readTimeout: 10000,
//get的extraData参数在上面处理过了 在这儿不需要再传一遍
extraData: method === http.RequestMethod.GET ? '' : data || {} as EmptyInterface,
//响应参数的类型,指定为对象后有BUG,当结果有问题时,项目会直接瘫痪
// expectDataType: http.HttpDataType.OBJECT,
//请求头
header: {
'Content-Type': 'application/json',
"Authorization": AppStorage.get(TOKEN_KEY) as string || ''
}
}
//发请求
try {
const res = await httpRequest.request(urlStr, config)
console.log('请求url地址', urlStr)
//res.responseCode响应状态码,这里的401还会认为是请求成功
if (res.responseCode === 401) {
//401 token超时
//删除持久化数据token
AppStorage.set<string>(TOKEN_KEY, '')
promptAction.showToast({ message: 'token超时!' })
//回登录
router.replaceUrl({
url: 'pages/Login/LoginPage'
})
//返回错误 终止
return Promise.reject(new Error('token超时!'))
} else if (res.responseCode === 404) {
promptAction.showToast({ message: '请求地址不正确!' })
return Promise.reject(new Error('请求地址不正确!'))
} else {
//指定为字符串,然后再转成一个对象,类型是不明确的要使用泛型,返回第一层+泛型,泛型的定义是一个类和之前的有所差距
const result = JSON.parse(res.result as string) as ResponsesData<T>
//再判断返回的状态码进行处理,不是200都是失败
if (result.code === 200) {
return result.data as T
} else {
promptAction.showToast({ message: '服务器异常!' })
return Promise.reject(new Error(result.msg))
}
}
} catch (error) {
promptAction.showToast({ message: error })
return Promise.reject(error)
}
//执行最后销毁请求
finally {
//销毁请求请求结束
httpRequest.destroy()
}
}
//封装一个静态类的方法出来使用
export class Request {
static get<T>(url: string, data?: object): Promise<T> {
return requestHttp<T>(url, http.RequestMethod.GET, data)
}
static post<T>(url: string, data?: object): Promise<T> {
return requestHttp<T>(url, http.RequestMethod.POST, data)
}
static put<T>(url: string, data?: object): Promise<T> {
return requestHttp<T>(url, http.RequestMethod.PUT, data)
}
static delete<T>(url: string, data?: object): Promise<T> {
return requestHttp<T>(url, http.RequestMethod.DELETE, data)
}
}