rentease-app/utils/request.js

320 lines
6.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 请求拦截器封装
* 统一处理HTTP请求和响应
*/
import { getBaseUrl, getTimeout, getStorageKey, statusCode } from '../config/index.js'
// 请求队列(用于取消重复请求)
const pendingRequests = new Map()
// 是否正在刷新token
let isRefreshing = false
// 等待刷新token的请求队列
let refreshSubscribers = []
/**
* 生成请求唯一标识
*/
const generateRequestKey = (config) => {
const { url, method, data } = config
return `${method}_${url}_${JSON.stringify(data || {})}`
}
/**
* 添加请求到队列
*/
const addPendingRequest = (config) => {
const key = generateRequestKey(config)
if (!pendingRequests.has(key)) {
pendingRequests.set(key, config)
}
}
/**
* 从队列移除请求
*/
const removePendingRequest = (config) => {
const key = generateRequestKey(config)
pendingRequests.delete(key)
}
/**
* 取消重复请求
*/
const cancelPendingRequest = (config) => {
const key = generateRequestKey(config)
if (pendingRequests.has(key)) {
return true
}
return false
}
/**
* 获取请求头
*/
const getHeaders = (needAuth = true) => {
const headers = {
'Content-Type': 'application/json'
}
if (needAuth) {
const token = uni.getStorageSync(getStorageKey('token'))
if (token) {
headers['Authorization'] = `Bearer ${token}`
}
}
return headers
}
/**
* 处理响应错误
* 优先使用后端返回的 message
*/
const handleResponseError = (response) => {
const { statusCode: code, data } = response
// 优先使用后端返回的 message
const message = data?.message
switch (code) {
case 400:
uni.showToast({
title: message || '请求参数错误',
icon: 'none'
})
break
case 401:
// Token过期清除登录状态并跳转登录页
uni.removeStorageSync(getStorageKey('token'))
uni.removeStorageSync(getStorageKey('userInfo'))
uni.showToast({
title: message || '登录已过期,请重新登录',
icon: 'none'
})
setTimeout(() => {
uni.reLaunch({
url: '/pages/login/login'
})
}, 1500)
break
case 403:
uni.showToast({
title: message || '没有权限执行此操作',
icon: 'none'
})
break
case 404:
uni.showToast({
title: message || '请求的资源不存在',
icon: 'none'
})
break
case 500:
uni.showToast({
title: message || '服务器错误,请稍后重试',
icon: 'none'
})
break
default:
uni.showToast({
title: message || '请求失败',
icon: 'none'
})
}
}
/**
* 统一请求方法
* @param {Object} options - 请求配置
* @param {boolean} needAuth - 是否需要认证
* @returns {Promise}
*/
const request = (options = {}, needAuth = true) => {
const baseUrl = getBaseUrl()
const timeout = getTimeout()
const config = {
url: options.url.startsWith('http') ? options.url : `${baseUrl}${options.url}`,
method: options.method || 'GET',
data: options.data || {},
header: { ...getHeaders(needAuth), ...options.header },
timeout: options.timeout || timeout
}
// 检查重复请求
if (cancelPendingRequest(config)) {
return Promise.reject(new Error('重复请求'))
}
// 添加请求到队列
addPendingRequest(config)
return new Promise((resolve, reject) => {
uni.request({
...config,
success: (response) => {
const { statusCode: code, data } = response
// 请求成功
if (code >= 200 && code < 300) {
// 如果后端返回的数据没有 code 字段,直接视为成功
if (!data.code || data.code === 200) {
resolve(data)
} else {
// 业务逻辑失败
uni.showToast({
title: data.message || '操作失败',
icon: 'none'
})
reject(data)
}
} else {
// HTTP错误
handleResponseError(response)
reject(response)
}
},
fail: (error) => {
uni.showToast({
title: '网络错误,请检查网络连接',
icon: 'none'
})
reject(error)
},
complete: () => {
// 从队列移除请求
removePendingRequest(config)
}
})
})
}
/**
* GET请求
*/
export const get = (url, params = {}, needAuth = true) => {
// 构建查询字符串
const queryString = Object.keys(params)
.map(key => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`)
.join('&')
const fullUrl = queryString ? `${url}?${queryString}` : url
return request({
url: fullUrl,
method: 'GET'
}, needAuth)
}
/**
* POST请求
*/
export const post = (url, data = {}, needAuth = true) => {
return request({
url,
method: 'POST',
data
}, needAuth)
}
/**
* PUT请求
*/
export const put = (url, data = {}, needAuth = true) => {
return request({
url,
method: 'PUT',
data
}, needAuth)
}
/**
* DELETE请求
*/
export const del = (url, data = {}, needAuth = true) => {
return request({
url,
method: 'DELETE',
data
}, needAuth)
}
/**
* 上传文件
*/
export const upload = (url, filePath, formData = {}, needAuth = true) => {
const baseUrl = getBaseUrl()
const token = uni.getStorageSync(getStorageKey('token'))
return new Promise((resolve, reject) => {
uni.uploadFile({
url: `${baseUrl}${url}`,
filePath,
name: 'file',
formData,
header: needAuth && token ? { 'Authorization': `Bearer ${token}` } : {},
success: (response) => {
const data = JSON.parse(response.data)
if (data.code === 200) {
resolve(data)
} else {
uni.showToast({
title: data.message || '上传失败',
icon: 'none'
})
reject(data)
}
},
fail: (error) => {
uni.showToast({
title: '上传失败',
icon: 'none'
})
reject(error)
}
})
})
}
/**
* 下载文件
*/
export const download = (url, needAuth = true) => {
const baseUrl = getBaseUrl()
const token = uni.getStorageSync(getStorageKey('token'))
return new Promise((resolve, reject) => {
uni.downloadFile({
url: `${baseUrl}${url}`,
header: needAuth && token ? { 'Authorization': `Bearer ${token}` } : {},
success: (response) => {
if (response.statusCode === 200) {
resolve(response)
} else {
reject(response)
}
},
fail: (error) => {
reject(error)
}
})
})
}
export default {
request,
get,
post,
put,
delete: del,
upload,
download
}