当前位置: 首页 > news >正文

【自用】request.ts 封装,带 token 过期后自动刷新 token 的功能

PC版本

封装

// 导入axios
import axios from 'axios'
import type { InternalAxiosRequestConfig, AxiosResponse, AxiosError } from 'axios'
import { ElMessage } from 'element-plus'
import router from '@/router'interface ResponseData<T = any> {code: numbermsg?: stringmessage?: stringdata?: T[key: string]: any
}interface TokenResponse {code: numberdata: {access_token: stringrefresh_token: stringtoken_type: string}msg?: string
}// 是否正在刷新token
let isRefreshing = false
// 重试队列,每一项将是一个待执行的函数形式
let requests: Array<(token: string) => void> = []const service = axios.create({baseURL: import.meta.env.VITE_APP_BASE_API as string,timeout: 3 * 60 * 1000,headers: {'Content-Type': 'application/json',},
})// 刷新token的函数const refreshToken = async (): Promise<string | null> => {// 如果refresh_token不存在,则返回nullconst refreshTokenValue = localStorage.getItem('refresh_token')if (!refreshTokenValue) {return null}// 使用axios发送post请求,刷新令牌try {const response = await post('/api_mani/auth/refresh', {refresh_token: refreshTokenValue,})const { access_token, refresh_token } = response.datalocalStorage.setItem('access_token', access_token)localStorage.setItem('refresh_token', refresh_token)return access_token} catch (error) {console.error('刷新令牌出错:', error)return null}
}// 2.请求拦截器
service.interceptors.request.use((config: InternalAxiosRequestConfig) => {const token = localStorage.getItem('access_token')if (token) {config.headers = config.headers || {}config.headers.Authorization = `Bearer ${token}`}return config},(error: AxiosError) => {console.log('request error: ', error)return Promise.reject(error)},
)// 3.响应拦截器
service.interceptors.response.use((res: AxiosResponse<ResponseData>) => {if (res.data.code === 401) {ElMessage.error(res.data.msg || res.data.message || '登录已过期,请重新登录')// 清除本地存储的tokenlocalStorage.removeItem('access_token')localStorage.removeItem('refresh_token')router.replace('/login')return Promise.reject(res.data) as any}if (res.data.code !== 200 && res.data.code !== 201) {ElMessage.error(res.data.msg || res.data.message || '请求失败')return Promise.reject(res.data) as any}return Promise.resolve(res.data)},async (error: AxiosError<ResponseData>) => {const originalRequest = error.config// 如果响应状态码是401(未授权)且原始请求存在if (error.response?.status === 401 && originalRequest) {// 如果不是刷新token的请求if (!originalRequest.url?.includes('/auth/refresh')) {if (!isRefreshing) {isRefreshing = true// 尝试刷新tokenconst newToken = await refreshToken()isRefreshing = falseif (newToken) {// 执行队列中的请求requests.forEach((cb) => cb(newToken))requests = []// 重新发起原始请求if (originalRequest.headers) {originalRequest.headers.Authorization = `Bearer ${newToken}`}return service(originalRequest)} else {// 刷新token失败,跳转到登录页ElMessage.error('登录已过期,请重新登录')localStorage.removeItem('access_token')localStorage.removeItem('refresh_token')router.replace('/login')}} else {// 将请求加入队列return new Promise((resolve) => {requests.push((token: string) => {if (originalRequest.headers) {originalRequest.headers.Authorization = `Bearer ${token}`}resolve(service(originalRequest))})})}}}// 其他错误处理console.log('response error: ', error)if (error.response?.data?.message) {ElMessage.error(error.response.data.message)} else if (error.message && error.message !== 'canceled') {// abort 前端取消接口请求时,报错信息为 canceled,不显示错误信息ElMessage.error(error.message)}return Promise.reject(error)},
)// get请求的封装
export function get<T = any>(url: string,params: Record<string, any> = {},signal?: AbortSignal,
): Promise<T> {return new Promise((resolve, reject) => {service({url,method: 'get',params,signal: signal,}).then((res: any) => {resolve(res as unknown as T)}).catch((err: any) => {reject(err)})})
}// post请求封装
export function post<T = any>(url: string,params: Record<string, any> = {},headers: Record<string, string> = { 'Content-Type': 'application/json' },signal?: AbortSignal,
): Promise<T> {return new Promise((resolve, reject) => {service({url,method: 'post',data: params,headers,signal: signal,}).then((res: any) => {resolve(res as unknown as T)}).catch((err: any) => {reject(err)})})
}export function patch<T = any>(url: string,params: Record<string, any> = {},headers: Record<string, string> = { 'Content-Type': 'application/json' },
): Promise<T> {return new Promise((resolve, reject) => {service({url,method: 'patch',data: params,headers,}).then((res: any) => {resolve(res as unknown as T)}).catch((err: any) => {reject(err)})})
}export function put<T = any>(url: string,params: Record<string, any> = {},headers: Record<string, string> = { 'Content-Type': 'application/json' },
): Promise<T> {return new Promise((resolve, reject) => {service({url,method: 'put',data: params,headers,}).then((res: any) => {resolve(res as unknown as T)}).catch((err: any) => {reject(err)})})
}export function DELETE<T = any>(url: string,params: Record<string, any> = {},headers: Record<string, string> = { 'Content-Type': 'application/json' },
): Promise<T> {return new Promise((resolve, reject) => {service({url,method: 'DELETE',data: params,headers,}).then((res: any) => {resolve(res as unknown as T)}).catch((err: any) => {reject(err)})})
}export default {get,post,patch,put,DELETE,
}

使用:

import { get, post } from '../service'
import type { API } from 'types/api'const api: API = {login: (params) => post(`/index/login`, params),
}
export default api
// types/api
interface Params {[key: string]: any
}// 定义 API 返回类型
type Response<T = any> = Promise<T>type ApiMethod = (params?: Params) => Responseexport interface API {[key: string]: ApiMethod
}

uni-app版本

封装

import Request from 'luch-request';interface ApiResponse<T = any> {data: T;code: number;msg: string;[key: string]: any;
}interface RequestConfig {params?: Record<string, any>; // GET 请求参数放在 config.params 中[key: string]: any;
}interface GetParams {[key: string]: string | number | boolean | undefined;
}interface PostData {[key: string]: any;
}const http = new Request({baseURL: import.meta.env.VITE_BASE_URL,timeout: 60000,
});// 请求拦截器
http.interceptors.request.use((config) => {const token = uni.getStorageSync('access_token');if (token) {config.header = config.header || {};config.header['Authorization'] = 'Bearer ' + token;}return config;},(error) => {return Promise.resolve(error);},
);
// 是否正在刷新token
let isRefreshing = false;
// 重试队列,每一项将是一个待执行的函数形式
let requests: Array<(token: string) => void> = [];
// 响应拦截器
http.interceptors.response.use(// @ts-ignore(response: any) => {console.log('response', response.data);return response.data as ApiResponse;},async (error) => {const originalRequest = error.config;console.log('response error', error);// 如果响应状态码是401(未授权)且原始请求存在if ((error.statusCode === 401 || error.data?.code === 401) &&originalRequest) {// 如果不是刷新token的请求console.log('originalRequest', originalRequest);if (!originalRequest.url?.includes('/auth/refresh')) {if (!isRefreshing) {isRefreshing = true;// 尝试刷新tokenconst newToken = await refreshToken();isRefreshing = false;if (newToken) {// 执行队列中的请求requests.forEach((cb) => cb(newToken));requests = [];// 重新发起原始请求if (originalRequest.header) {originalRequest.header.Authorization = `Bearer ${newToken}`;}return http.request(originalRequest);} else {// 刷新token失败,跳转到登录页uni.showToast({title: '登录已过期,请重新登录',icon: 'none',duration: 2000,});uni.removeStorageSync('access_token');uni.removeStorageSync('refresh_token');uni.reLaunch({url: '/pages/login/index',});}} else {// 将请求加入队列return new Promise((resolve) => {requests.push((token: string) => {if (originalRequest.header) {originalRequest.header.Authorization = `Bearer ${token}`;}resolve(http.request(originalRequest));});});}}} else {const err = error?.data?.msg || error.errMsg;console.log('err', err);uni.showToast({title: err,icon: 'none',duration: 2000,});return Promise.resolve(error);}},
);const request = {/*** GET 请求* @param url 请求地址* @param params 查询参数* @param config 请求配置*/get<T = any>(url: string,params?: GetParams,config?: RequestConfig,): Promise<ApiResponse<T>> {return http.get(url, {params,...config,});},/*** POST 请求* @param url 请求地址* @param data 请求数据* @param config 请求配置*/post<T = any>(url: string,data?: PostData,config?: RequestConfig,): Promise<ApiResponse<T>> {return http.post(url, data, config);},
};const refreshToken = async (): Promise<string | null> => {// 如果refresh_token不存在,则返回nullconst refreshTokenValue = uni.getStorageSync('refresh_token');if (!refreshTokenValue) {return null;}// 使用axios发送post请求,刷新令牌try {const response = await request.post('/app/auth/refresh', {refresh_token: refreshTokenValue,});const { access_token, refresh_token } = response.data;uni.setStorageSync('access_token', access_token);uni.setStorageSync('refresh_token', refresh_token);return access_token;} catch (error) {console.error('刷新令牌出错:', error);return null;}
};export default request;

使用

import request from '../utils/request';
// 用户相关API
export default {// 登录login: (data: { phone: string; code: string }) => {return request.post('/app/login', data);},
};
http://www.dtcms.com/a/469899.html

相关文章:

  • 成都定制网站建设服合肥公司注册地址
  • 分布式事务在前后端分离场景下的最终一致性实现
  • 农产品电子商务网站建设要求锦州网站建设公司
  • SSH命令建立隧道
  • [GazeTracking] 依赖项管理 | Docker化执行环境
  • uniapp web-view相互通信方法
  • (2)Kafka架构原理与存储机制
  • uniapp学习【项目创建+项目结构解析】
  • 虚拟机所需的硬件功能在目标主机上不受支持或已禁用:*长模式:对于支持64位客户机操作系统而言是必需的。
  • Uniapp微信小程序开发:http请求封装。
  • 个人可以做商城网站吗合肥制作网站价格
  • 网站制作的前期主要是做好什么工作网站的构思
  • java每小时调动一次,生成任务,基于corn表达式动态调动任务执行
  • 网站模板兼容手机端市场推广是做什么的
  • 企业微信防封防投诉拦截系统:从痛点解决到技术实现
  • vue的组件通信
  • 掌握PINN:从理论到实战的神经网络进阶!!
  • wordpress thremeseo推广排名软件
  • 安平县哪里做网站建立公司网站视频
  • PostgreSql ALL 与 ANY 区别
  • 解决 husky > pre-commit hook failed (add --no-verify to bypass)
  • 模拟器抓包
  • (数据结构)线性表(下):链表分类及双向链表的实现
  • 阿里云短信服务配置说明
  • Java+SpringBoot+Dubbo+Nacos快速入门
  • 【开题答辩全过程】以 办公管理系统为例,包含答辩的问题和答案
  • 天创网站做网站 数据标准
  • 做除尘骨架的网站网页一般用什么语言编写
  • SciPy 常量模块
  • 记录一次在Win7系统中使用C#中的HttpWebRequest连接缓慢、超时等问题(httpclient和restsharp也存在同样的问题)