271 lines
6.7 KiB
JavaScript
271 lines
6.7 KiB
JavaScript
// HTTP请求配置和拦截器
|
||
// 基于uView-next的luch-request库实现
|
||
|
||
import { HTTP_CONFIG, checkApiAuth, AUTH_REQUIRED_APIS } from './config.js'
|
||
|
||
/**
|
||
* 清理认证数据
|
||
*/
|
||
function clearAuthData() {
|
||
uni.removeStorageSync(HTTP_CONFIG.storageKeys.token)
|
||
uni.removeStorageSync(HTTP_CONFIG.storageKeys.refreshToken)
|
||
uni.removeStorageSync(HTTP_CONFIG.storageKeys.userInfo)
|
||
uni.removeStorageSync('loginStep')
|
||
uni.removeStorageSync('wxLoginCode')
|
||
uni.removeStorageSync('wxUserInfo')
|
||
uni.removeStorageSync('loginDate')
|
||
}
|
||
|
||
/**
|
||
* 检查接口是否需要强制登录
|
||
* @param {String} url 接口URL
|
||
* @returns {Boolean} 是否需要强制登录
|
||
*/
|
||
function checkAuthRequiredApi(url) {
|
||
// 移除查询参数,只保留路径
|
||
const path = url.split('?')[0]
|
||
|
||
// 检查是否在需要强制登录的列表中
|
||
for (const authApi of AUTH_REQUIRED_APIS) {
|
||
// 支持通配符匹配
|
||
if (authApi.includes('*')) {
|
||
const regex = new RegExp('^' + authApi.replace(/\*/g, '.*') + '$')
|
||
if (regex.test(path)) {
|
||
return true
|
||
}
|
||
} else if (path === authApi || path.startsWith(authApi + '/')) {
|
||
return true
|
||
}
|
||
}
|
||
|
||
return false
|
||
}
|
||
|
||
/**
|
||
* 检查用户是否已完成登录
|
||
* @returns {Boolean} 是否已完成登录
|
||
*/
|
||
function isUserLoggedIn() {
|
||
const token = uni.getStorageSync(HTTP_CONFIG.storageKeys.token)
|
||
const loginStep = uni.getStorageSync('loginStep')
|
||
|
||
// 检查是否有有效的token和完成的登录流程
|
||
return !!(token && (loginStep === 'profile_completed' || loginStep === 'phone_authed'))
|
||
}
|
||
|
||
/**
|
||
* 尝试自动登录
|
||
* @returns {Promise<Boolean>} 是否登录成功
|
||
*/
|
||
async function tryAutoLogin() {
|
||
try {
|
||
return await autoLogin()
|
||
} catch (error) {
|
||
console.error('自动登录失败:', error)
|
||
return false
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 处理需要鉴权的情况
|
||
*/
|
||
function handleAuthRequired() {
|
||
const loginStep = uni.getStorageSync('loginStep')
|
||
|
||
// 根据当前登录状态决定跳转页面
|
||
let targetPage = '/pages/login/login'
|
||
let toastMessage = '请先登录'
|
||
|
||
switch (loginStep) {
|
||
case 'wx_logged':
|
||
targetPage = '/pages/auth/phone-auth'
|
||
toastMessage = '请完成手机号授权'
|
||
break
|
||
case 'phone_authed':
|
||
case 'phone_skipped':
|
||
targetPage = '/pages/profile/user-info?mode=setup'
|
||
toastMessage = '请完善个人信息'
|
||
break
|
||
default:
|
||
targetPage = '/pages/login/login'
|
||
toastMessage = '请先登录'
|
||
}
|
||
|
||
// 显示提示并跳转
|
||
uni.showToast({
|
||
title: toastMessage,
|
||
icon: 'none',
|
||
duration: 1500
|
||
})
|
||
|
||
setTimeout(() => {
|
||
uni.navigateTo({ url: targetPage }).catch(() => {
|
||
uni.reLaunch({ url: targetPage })
|
||
})
|
||
}, 1500)
|
||
}
|
||
|
||
/**
|
||
* 初始化HTTP配置
|
||
* @param {Object} vm Vue实例,用于访问vuex等全局状态
|
||
*/
|
||
export default (vm) => {
|
||
// 初始化请求配置
|
||
uni.$u.http.setConfig((config) => {
|
||
config.baseURL = HTTP_CONFIG.baseURL
|
||
config.timeout = HTTP_CONFIG.timeout
|
||
config.header = {
|
||
'Content-Type': 'application/json;charset=UTF-8'
|
||
}
|
||
return config
|
||
})
|
||
|
||
// 请求拦截器
|
||
uni.$u.http.interceptors.request.use((config) => {
|
||
// 初始化请求拦截器时,会执行此方法,此时data为undefined,赋予默认{}
|
||
config.data = config.data || {}
|
||
config.custom = config.custom || {}
|
||
|
||
|
||
|
||
// 自动检查接口是否需要鉴权(如果没有明确指定)
|
||
if (config.custom.auth === undefined) {
|
||
config.custom.auth = checkApiAuth(config.url)
|
||
}
|
||
|
||
// 检查是否为需要强制登录的接口
|
||
const isAuthRequiredApi = checkAuthRequiredApi(config.url)
|
||
|
||
// 如果是需要强制登录的接口,检查登录状态
|
||
if (isAuthRequiredApi) {
|
||
if (!isUserLoggedIn()) {
|
||
console.log('访问需要鉴权的接口但未完成登录,跳转到登录流程:', config.url)
|
||
handleAuthRequired()
|
||
return Promise.reject({
|
||
message: '需要登录',
|
||
code: 'AUTH_REQUIRED',
|
||
url: config.url
|
||
})
|
||
}
|
||
}
|
||
|
||
// 添加Authorization请求头
|
||
if (config.custom.auth !== false) {
|
||
const token = uni.getStorageSync(HTTP_CONFIG.storageKeys.token)
|
||
if (token) {
|
||
config.header.Authorization = `Bearer ${token}`
|
||
}
|
||
}
|
||
|
||
// 根据custom参数配置是否显示loading
|
||
if (config.custom.loading !== false) {
|
||
uni.showLoading({
|
||
title: config.custom.loadingText || '请稍候...',
|
||
mask: true
|
||
})
|
||
}
|
||
|
||
return config
|
||
}, config => {
|
||
// 请求错误处理
|
||
// 如果是AUTH_REQUIRED错误,不显示loading
|
||
if (config.code === 'AUTH_REQUIRED') {
|
||
// 不显示loading,因为会跳转页面
|
||
return Promise.reject(config)
|
||
}
|
||
return Promise.reject(config)
|
||
})
|
||
|
||
// 响应拦截器
|
||
uni.$u.http.interceptors.response.use((response) => {
|
||
/* 对响应成功做点什么 可使用async await 做异步操作*/
|
||
|
||
// 隐藏loading
|
||
uni.hideLoading()
|
||
|
||
const data = response.data
|
||
const custom = response.config?.custom
|
||
|
||
// 统一的响应数据处理
|
||
if (data.code !== undefined) {
|
||
// 如果服务端返回的状态码不等于200,则reject()
|
||
if (data.code !== 200) {
|
||
// 如果没有显式定义custom的toast参数为false的话,默认对报错进行toast弹出提示
|
||
if (custom?.toast !== false) {
|
||
uni.$u.toast(data.message || '请求失败')
|
||
}
|
||
|
||
// 特殊状态码处理
|
||
if (data.code === 401) {
|
||
// token过期,清除本地认证信息并跳转到登录流程
|
||
clearAuthData()
|
||
handleAuthRequired()
|
||
return Promise.reject(data)
|
||
}
|
||
|
||
// 如果需要catch返回,则进行reject
|
||
if (custom?.catch) {
|
||
return Promise.reject(data)
|
||
} else {
|
||
// 否则返回一个pending中的promise,请求不会进入catch中
|
||
return new Promise(() => {})
|
||
}
|
||
}
|
||
}
|
||
|
||
// 返回处理后的数据
|
||
return data.data !== undefined ? data.data : data
|
||
}, (response) => {
|
||
/* 对响应错误做点什么 (statusCode !== 200)*/
|
||
|
||
// 隐藏loading
|
||
uni.hideLoading()
|
||
|
||
const custom = response.config?.custom
|
||
|
||
// 网络错误处理
|
||
let errorMessage = '网络错误,请检查网络连接'
|
||
|
||
if (response.statusCode) {
|
||
switch (response.statusCode) {
|
||
case 400:
|
||
errorMessage = '请求参数错误'
|
||
break
|
||
case 401:
|
||
errorMessage = '未授权,请重新登录'
|
||
clearAuthData()
|
||
handleAuthRequired()
|
||
break
|
||
case 403:
|
||
errorMessage = '拒绝访问'
|
||
break
|
||
case 404:
|
||
errorMessage = '请求地址不存在'
|
||
break
|
||
case 500:
|
||
errorMessage = '服务器内部错误'
|
||
break
|
||
case 502:
|
||
errorMessage = '网关错误'
|
||
break
|
||
case 503:
|
||
errorMessage = '服务不可用'
|
||
break
|
||
case 504:
|
||
errorMessage = '网关超时'
|
||
break
|
||
default:
|
||
errorMessage = `连接错误${response.statusCode}`
|
||
}
|
||
}
|
||
|
||
// 显示错误提示
|
||
if (custom?.toast !== false) {
|
||
uni.$u.toast(errorMessage)
|
||
}
|
||
|
||
console.error('请求错误:', response)
|
||
return Promise.reject(response)
|
||
})
|
||
}
|