react native token失效 刷新机制

复制代码
// services/http.ts
import axios, {
  AxiosInstance,
  AxiosRequestConfig,
  AxiosResponse,
  AxiosError,
} from 'axios';
import { BaseResponse, RequestConfig } from '../types/http';
import { Alert, DeviceEventEmitter } from 'react-native';

import LocalStorage from '@/service/LocalStorage.ts';
import { tokenType } from '@/types/auth.ts';
import AuthService from '@/service/AuthService';
import { config } from './config';
import AuthStorageService from '@/service/AuthStorageService';

class HttpService {
  private authStorage = AuthStorageService.getInstance();
  private static instance: HttpService;
  private axiosInstance: AxiosInstance;
  private baseURL: string = 'http://cwsw.xathinksoft.com:30000'; // 替换为你的API基础地址
  private isRefreshing = false; // 是否正在重新登录
  private failedQueue: Array<{
    resolve: (value: any) => void;
    reject: (error: any) => void;
  }> = []; // 请求队列

  private constructor() {
    this.axiosInstance = axios.create({
      baseURL: this.baseURL,
      timeout: 10000000 * 60, // 60秒超时
      headers: {
        'Content-Type': 'application/json',
        Accept: 'application/json',
      },
    });

    this.setupInterceptors();
  }

  public static getInstance(): HttpService {
    if (!HttpService.instance) {
      HttpService.instance = new HttpService();
    }
    return HttpService.instance;
  }

  // 设置拦截器
  private setupInterceptors(): void {
    // 请求拦截器

    this.axiosInstance.interceptors.request.use(
      // @ts-ignore
      async (config: AxiosRequestConfig) => {
        // 可以在这里添加全局请求逻辑,例如添加token
        const token: tokenType | null = await LocalStorage.getItem<tokenType>(
          'token',
        );
        if (token && config.headers) {
          config.headers.Authorization = `Bearer ${token?.access_token}`
        }

        // 如果需要显示加载提示
        if ((config as RequestConfig).showLoading) {
          // 这里可以添加加载提示逻辑
          console.log('显示加载提示');
        }

        return config;
      },
      (error: AxiosError) => {
        return Promise.reject(error);
      },
    );

    // 响应拦截器
    this.axiosInstance.interceptors.response.use(
      (response: AxiosResponse<any>) => {
        console.log('响应拦截器', response);
        // 隐藏加载提示
        if ((response.config as RequestConfig).showLoading) {
          console.log('隐藏加载提示');
        }

        if (response.data?.code === 600) {
          // navigator.navigate('Login');
          
             return this.handleTokenExpired(response);
        }
        if (response.data && typeof response.data.success !== 'undefined') {
          // 处理响应数据
          if (response.data.success) {
            return response.data;
          } else {
            // 业务逻辑错误
            this.handleBusinessError(
              response.data,
              response.config as RequestConfig,
            );
            return Promise.reject(response.data);
          }
        }

        return response.data;
      },
      (error: AxiosError) => {
        console.log('响应拦截器', error);
        // 隐藏加载提示
        if (error.config && (error.config as RequestConfig).showLoading) {
          console.log('隐藏加载提示');
        }

        // 处理HTTP错误
        this.handleHttpError(error, error.config as RequestConfig);
        return Promise.reject(error);
      },
    );
  }

    // 处理 Token 过期
    private async handleTokenExpired(response: AxiosResponse): Promise<any> {
      const originalRequest = response.config as any;
  
      // 如果正在重新登录,将请求加入队列
      if (this.isRefreshing) {
        return new Promise((resolve, reject) => {
          this.failedQueue.push({ resolve, reject });
        })
          .then((newToken) => {
            if (originalRequest.headers) {
              originalRequest.headers.Authorization = `Bearer ${newToken}`;
            }
            return this.axiosInstance(originalRequest);
          })
          .catch((err) => {
            return Promise.reject(err);
          });
      }
  
      this.isRefreshing = true;
  
      try {
        // 调用登录接口刷新 Token
        const newToken = await this.refreshTokenByLogin();
      
        console.log('✅ Token 刷新成功');
  
        // 更新原始请求的 Authorization header
        if (originalRequest.headers) {
          originalRequest.headers.Authorization = `Bearer ${newToken}`;
        }
  
        // 重试原始请求
        const retryResponse = await this.axiosInstance(originalRequest);
        
        // 处理队列中的请求
        this.processQueue(null, newToken);
        
        console.log('retryResponse', retryResponse)
        return retryResponse;
      } catch (error) {
        // 重新登录失败
        this.processQueue(error, null);
        await this.handleLoginFailed();
        return Promise.reject(response.data);
      } finally {
        this.isRefreshing = false;
        this.failedQueue = [];
      }
    }
  
    // 通过登录接口刷新 Token
    private async refreshTokenByLogin(): Promise<string> {

      try {
        // 获取保存的登录凭据
        const credentials = AuthService.getCurrentUser();
        if (!credentials) {
          throw new Error('没有找到登录信息');
        }
  
        console.log('44444444🔄 自动重新登录中...', credentials);
        
        // 调用登录接口
        const loginResponse = await http.get(
          `${config.loginApi}?loginName=${credentials.LoginName}&password=${credentials.Phone}&tenant=cw1203`,
        );

        console.log('loginResponse', loginResponse)
  
        if (loginResponse.Status && loginResponse.Data) {
          await this.authStorage.saveLoginResponse(loginResponse.Data);
          await LocalStorage.setItem<tokenType>('token', loginResponse.Data.Token);
  
    
          
          return loginResponse?.Data?.Token?.access_token;
        } else {
          throw new Error(loginResponse.data.message || '登录失败');
        }
      } catch (error: any) {
        console.error('❌ 自动重新登录失败:', error);
        throw new Error(`自动重新登录失败: ${error.message}`);
      }
    }
  
    // 处理请求队列
    private processQueue(error: any, token: string | null): void {
      this.failedQueue.forEach(({ resolve, reject }) => {
        if (error) {
          reject(error);
        } else {
          resolve(token);
        }
      });
    }
  
    // 处理登录失败
    private async handleLoginFailed(): Promise<void> {
      console.log('❌ 自动重新登录失败,需要手动登录');
      
      // 清除本地存储的认证信息
      await LocalStorage.removeItem('token');
      
      // 发出全局事件,通知需要手动登录
      DeviceEventEmitter.emit('LOGIN_REQUIRED', {
        message: '登录已过期,请重新登录',
        timestamp: Date.now(),
      });
    }
  

  


  // 处理业务逻辑错误
  private handleBusinessError(
    errorData: BaseResponse,
    config?: RequestConfig,
  ): void {
    const { showError = true, customErrorHandler } = config || {};

    if (customErrorHandler) {
      customErrorHandler(errorData);
      return;
    }

    if (showError) {
      Alert.alert('操作失败', errorData.message || '未知错误');
    }
  }

  // 处理HTTP错误
  private handleHttpError(error: AxiosError, config?: RequestConfig): void {
    const { showError = true, customErrorHandler } = config || {};

    if (customErrorHandler) {
      customErrorHandler(error);
      return;
    }

    if (!showError) return;

    let errorMessage = '网络请求失败';

    if (error.response) {
      // 服务器返回了响应,但状态码不在2xx范围内
      switch (error.response.status) {
        case 401:
          errorMessage = '未授权,请重新登录';
          // 可以在这里处理token过期逻辑
          break;
        case 403:
          errorMessage = '拒绝访问';
          break;
        case 404:
          errorMessage = '请求的资源不存在';
          break;
        case 500:
          errorMessage = '服务器错误';
          break;
        default:
          errorMessage = `服务器错误: ${error.response.status}`;
      }
    } else if (error.request) {
      // 请求已发出,但没有收到响应
      errorMessage = '网络连接超时,请检查网络设置';
    } else {
      // 请求配置出错
      errorMessage = '请求配置错误';
    }

    Alert.alert('错误', errorMessage);
  }

  // 通用请求方法
  public async request<T = any>(config: RequestConfig): Promise<T> {
    try {
      return await this.axiosInstance.request(config);
    } catch (error) {
      throw error;
    }
  }

  // GET请求
  public async get<T = any>(
    url: string,
    params?: any,
    config?: Omit<RequestConfig, 'url' | 'params' | 'method'>,
  ): Promise<T> {
    return this.request<T>({
      url,
      method: 'GET',
      params,
      ...config,
    });
  }

  // POST请求
  public async post<T = any>(
    url: string,
    data?: any,
    config?: Omit<RequestConfig, 'url' | 'data' | 'method'>,
  ): Promise<T> {
    return this.request<T>({
      url,
      method: 'POST',
      data,
      ...config,
    });
  }

  // PUT请求
  public async put<T = any>(
    url: string,
    data?: any,
    config?: Omit<RequestConfig, 'url' | 'data' | 'method'>,
  ): Promise<T> {
    return this.request<T>({
      url,
      method: 'PUT',
      data,
      ...config,
    });
  }

  // DELETE请求
  public async delete<T = any>(
    url: string,
    params?: any,
    config?: Omit<RequestConfig, 'url' | 'params' | 'method'>,
  ): Promise<T> {
    return this.request<T>({
      url,
      method: 'DELETE',
      params,
      ...config,
    });
  }

  // PATCH请求
  public async patch<T = any>(
    url: string,
    data?: any,
    config?: Omit<RequestConfig, 'url' | 'data' | 'method'>,
  ): Promise<T> {
    return this.request<T>({
      url,
      method: 'PATCH',
      data,
      ...config,
    });
  }
}

export const http = HttpService.getInstance();

按上面的写了 发现 没获取到数据(上面已经是正确的代码)

最后调试发现是:

最开始代码写的是 return retryResponse.data

因为这里已经返回的是 retryResponse.data 的数据, 直接return retryResponse即可

改为调试的结果为: ---就不会报错了

遇见问题不要怕 ,耐心分析

相关推荐
Sailing2 小时前
🚀 Promise.then 与 async/await 到底差在哪?(这次彻底讲明白)
前端·javascript·面试
鹤鸣的日常2 小时前
Vue + element plus 二次封装表格
前端·javascript·vue.js·elementui·typescript
北慕阳2 小时前
速成Vue,自己看
前端·javascript·vue.js
aiguangyuan2 小时前
React中Context 的作用及原理
javascript·react·前端开发
小白每天学一点3 小时前
微信小程序开发学习-8
javascript·css·微信小程序·小程序·html
by__csdn3 小时前
Electron入门:跨平台桌面开发指南
前端·javascript·vue.js·typescript·electron·html
星离~11 小时前
Vue响应式原理详解:从零实现一个迷你Vue
前端·javascript·vue.js