原生小程序的请求封装
我的小程序全部是基于原生写法。其中因为有大量的请求需要使用。所以将所有的请求封装成了一个请求文件。这里将这个请求文件分享出来。
在小程序建立以下文件
utils/request.js
代码如下:
/************ API 定义 ************/
/**
* api入口定义
*/
const host = 'http://supply.test',
api_entry = host + '/api/weapp'
/**
* 登录
* @param {*} data
* @returns
*/
async function login() {
// 登录参数
async function getLoginParams() {
const wxLogin = () => new Promise((res, rej) => wx.login({ success: r => res(r), fail: e => rej(e) }))
try {
const { code } = await wxLogin(),
{ model: device_name } = wx.getSystemInfoSync()
return { code, device_name }
} catch (e) {
wx.showModal({ title: '登录失败,请稍后再试' })
DEBUG && console.log('[request] getLoginParams, wxLogin fail:', e)
return;
}
}
const login_params = await getLoginParams()
// 登录接口
const { token, expire_in = 604800 } = await repository('/authorizations', post(login_params))
// 登录成功并设置token缓存
setAccessToken(token, expire_in)
return token
}
/**
* 更新token
* @param {*} data
* @returns
*/
async function refreshToken() {
const { token, expire_in = 604800 } = await repository('/authorizations/current', post('', withToken))
// 登录成功并设置token缓存
setAccessToken(token, expire_in)
return token
}
// 这里是方法列表,你可以在下面增加你的方法,这里只是做个示范
async function getCustom() {
return await repository('/settings/custom', get(), { fresher: false, useCache: true })
}
// 这里导出上面的方法列表
export {
host,
getAccessToken,
getCustom,
login
}
/************ API 定义结束 ************/
/************ 可修改部分 ************/
const DEBUG = false
// 缓存 key 定义
const KEY_ACCESS_TOKEN = 'access_token',
KEY_TOKEN_EXPIRE = 'access_token_expired_at'
/**
* 缓存提供者
* 可以使用其它缓存接口,需要实现get、set方法
*/
const cacheProvider = {
get: async (key) => {
return await wx.getStorage({ key: 'repository/' + key }).then(res => res.data).catch(e => null)
},
set: async (key, data) => {
await wx.setStorage({ key: 'repository/' + key, data })
}
}
// repository 配置
const repositoryConfig = {
// 缓存提供者
cacheProvider,
// 需要更新数据
fresher: true,
// 使用系统缓存
useCache: false,
// 验证结果
validate: true,
// 只返回结果
onlyFetchedData: true,
// 刷新缓存
refreshCache: false,
}
/**
* 定义获取器
*/
function fetcher(method, data, before) {
method = method.toUpperCase()
if (!['GET', 'POST', 'PUT', 'DELETE'].includes(method))
throw new Error('[request] not allow method: ' + method)
return async (url) => {
let option = {
url, data, method, header: {
'X-Requested-With': 'XMLHttpRequest',
'Accept': 'Application/json',
}
}
if (!Array.isArray(before)) before = [before]
for (const i in before) {
if (typeof before[i] === 'function')
option = await before[i](option)
}
return wxRequest(option).then(responseHandler)
}
}
const get = (d, be) => fetcher('get', d || '', [urlCon, be])
const post = (d, be) => fetcher('post', d || '', [urlCon, be])
const put = (d, be) => fetcher('put', d || '', [urlCon, be])
const del = (d, be) => fetcher('delete', d || '', [urlCon, be])
const urlCon = (option) => ({ ...option, url: api_entry + option.url })
const withToken = async (option) => ({ ...option, header: { 'Authorization': 'Bearer ' + await getAccessToken() } })
/**
* 响应错误处理
* 返回对象 { error }
* @param {Object} err
*/
function errorHandler(err) {
if (err.response.statusCode == 422) {
wx.showToast({ title: '提交内容错误', icon: 'error' })
return { error: err }
} else {
wx.showToast({ title: err.message, icon: 'error' })
return { error: err }
}
}
/**
*
* @param {data, error, response} params
* @returns
*/
function checkError(result) {
let { error, response } = result
return new Promise(resolve => error ? errorHandler({ ...error, response }) : resolve(result))
}
/**
* 响应处理方法
* 根据HTTP响应状态码处理响应内容,当错误时 error 不为空,即`!!error === true`
* 返回对象 { data, error, response }
* @param {*} response
* @returns
*/
function responseHandler(response) {
// DEBUG && console.log('[request] responseHandler; response:', response)
if (200 <= response.statusCode && response.statusCode < 300) {
return { data: response.data, error: null, response }
}
if (400 <= response.statusCode && response.statusCode < 500) {
DEBUG && console.log('[request] responseHandler, request error; response:', response)
// 当未提供正确token时,响应码为401
if (response.statusCode === 401) {
DEBUG && console.log('[request] responsehandler, token 无效或过期')
}
return { data: null, error: response.data, response }
}
if (500 <= response.statusCode && response.statusCode < 600) {
DEBUG && console.log('[request] responseHandler, server error; response:', response)
return { data: null, error: response.data, response }
}
}
/************ 可修改部分结束 ************/
/************ 约定部分 ************/
/**
* 数据仓库
* 返回内容为fetcher的结果
* @param {*} key
* @param {*} fetcher
* @param {*} _option
* @returns
*/
async function repository(key, fetcher, _option) {
_option = repositoryConfigure(_option || {})
const fresher = _option.fresher === true,
useCache = _option.useCache === true,
validate = _option.validate === true,
cacheProvider = useCache && _option.cacheProvider ? _option.cacheProvider : defaultRepositories,
onlyFetchedData = _option.onlyFetchedData === true,
refreshCache = _option.refreshCache === true
// key = stringifyKey(key)
if (typeof key === 'function') key = key()
if (Array.isArray(key)) key = JSON.stringify(key)
let result
if (!fresher) {
result = await cacheProvider.get(key)
}
// 获取数据
if (!result || refreshCache) {
result = await fetcher(key).then(r => validate ? checkError(r) : r)
DEBUG && console.log('[request] repository, fetched: ', key, result)
await cacheProvider.set(key, result)
} else {
DEBUG && console.log('[request] repository, get from cache:', key, result)
}
function mutate(data) {
DEBUG && console.log('[request] repository, mutate', data)
return repository(key, async () => ({ data }), { ..._option, refreshCache: true, onlyFetchedData: true })
}
function refresh() {
DEBUG && console.log('[request] repository, refresh')
return repository(key, fetcher, { ..._option, refreshCache: true })
}
return onlyFetchedData ? result.data : { ...result, mutate, refresh };
}
const repositoryConfigure = (config) => ({ ...repositoryConfig, ...config })
const defaultRepositories = new Map()
/**
* 微信请求promise封装
* @param {Object} options
* @returns {Object}
*/
async function wxRequest({ url, data, method, header }) {
return await new Promise((resolve, reject) =>
wx.request({ url, data, method, header, success: res => resolve(res), fail: err => reject(err) })
)
}
/**
* 获取token
* @param {Boolean} fresher
*/
async function getAccessToken(retry = 1) {
// 获取缓存 token
let token = (await cacheProvider.get(KEY_ACCESS_TOKEN)),
expire = await cacheProvider.get(KEY_TOKEN_EXPIRE)
DEBUG && console.log('[request] getAccessToken, after cacheProvider; token, expire: ', token, expire)
if (!token) {
DEBUG && console.log('[request] getAccessToken, login')
// 登录获取token
await login()
}
// 检查过期时间
else if (expire <= timestramp()) {
DEBUG && console.log('[request] getAccessToken, refresh')
// 刷新token
await refreshToken()
}
else {
return token
}
return retry > 0 ? getAccessToken(0) : 'no_token'
}
/**
* 保存 token
* @param {String} token
* @param {Number} expire_in
*/
function setAccessToken(token, expire_in) {
cacheProvider.set(KEY_ACCESS_TOKEN, token)
cacheProvider.set(KEY_TOKEN_EXPIRE, timestramp() + expire_in)
}
/**
* 获取时间戳
* 单位:秒
* @returns
*/
function timestramp() {
return parseInt((new Date().getTime() / 1000).toFixed(0))
}
/************ 约定部分结束 ************/
使用的时候只需引入在当前文件export
的的方法即可。
文档见:https://doc.wyz.xyz/pages/39ef55/
小程序开发交流 QQ 群:156516399