flutter dio^5.3.3实现刷新token

业务场景:并发请求A、B、C三个接口,但是这个三个接口都需要携带token才能请求到正确结果,所以我们的正确思维应该是,例如A接口请求到了,但是返回401没有权限,这个时候就需要拦截B和C两个接口不去执行,然后A接口返回401之后我们去请求tokne,拿到token后还有把A接口重试一下,也就是重新请求一次,最后,我们再放行B和C接口的请求。dio^4.0的版本里才有Lock这个类,到了dio^5.0的版本,作者希望使用QueuedInterceptorsWrapper去拦截队列请求。但是一定要注意,整个功能我们需要两个dio的实例,一个 负责正常的业务请求,另一个dio实例负责只请求token的任务,为什么要这样做,举个例子,一群人排长队拉屎,突然没有纸了,这个时候你派谁去拿纸啊?只能找一个没有拉屎的人去给你们排队拉屎的人拿纸去,好了,下面是代码:

import 'package:dio/dio.dart';
import 'package:flutter/cupertino.dart';
import 'package:hyys_app/api/index.dart';
import 'package:hyys_app/utils/constant.dart';
import 'package:shared_preferences/shared_preferences.dart';

class AuthInterceptor extends QueuedInterceptorsWrapper {
  final Dio tokenDio;
  final int _statusCode = 401;

  AuthInterceptor({required this.tokenDio});

  @override
  void onRequest(RequestOptions options, RequestInterceptorHandler handler) async {
    /********************************************携带token请求****************************************************************/
    SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
    String? token = sharedPreferences.getString(Constant.tokenKey);

    options.headers = {'token': token};

    if (options.method == 'GET') {
      int timestamp = DateTime.now().millisecondsSinceEpoch;
      options.queryParameters = {'_t': timestamp, ...options.queryParameters};
    }
    /********************************************携带token请求****************************************************************/

    Response response = await tokenDio.request(options.path);

    if (response.data['code'] == _statusCode) {
      debugPrint('${options.path}没有权限,需要刷新token');
      bool isAuth = await refreshToken();

      debugPrint('刷新token完成');
      if (isAuth) {
        Response response22 = await _retry(options);
        debugPrint('重试的路径${options.path}');
        debugPrint('重试的响应${response22.data.toString()}');

        handler.resolve(response22);
      }
    } else {
      handler.next(options);
    }
  }

  @override
  void onResponse(Response response, ResponseInterceptorHandler handler) {
    handler.next(response);
  }

  @override
  void onError(DioException err, ErrorInterceptorHandler handler) {
    // TODO: implement onError
    super.onError(err, handler);
  }

  Future<bool> refreshToken() async {
    debugPrint('刷新token开始');
    String mpLoginURL = '${Api.baseUrl}/user/mpLogin';
    Map<String, dynamic> params = {"phoneNumber": '17733405693'};

    Response response = await tokenDio.request(mpLoginURL, queryParameters: params);
    if (response.data['code'] == 0) {
      SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
      String token = response.data['data']['token'];

      await sharedPreferences.setString(Constant.tokenKey, token);

      return true;
    } else {
      return false;
    }
  }

  Future<Response<dynamic>> _retry(RequestOptions requestOptions) async {
    return tokenDio.request<dynamic>(requestOptions.path);
  }
}

import 'package:dio/dio.dart';
import 'package:hyys_app/utils/constant.dart';
import 'package:shared_preferences/shared_preferences.dart';

class TokenInterceptors extends Interceptor {
  @override
  void onRequest(RequestOptions options, RequestInterceptorHandler handler) async {
    /********************************************携带token请求****************************************************************/
    SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
    String? token = sharedPreferences.getString(Constant.tokenKey);

    options.headers = {'token': token};

    if (options.method == 'GET') {
      int timestamp = DateTime.now().millisecondsSinceEpoch;
      options.queryParameters = {'_t': timestamp, ...options.queryParameters};
    }
    handler.next(options);
    /********************************************携带token请求****************************************************************/
  }
}

import 'dart:convert';

import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import 'package:hyys_app/request/auth_interceptor.dart';
import 'package:hyys_app/request/custom_transformer.dart';
import 'package:hyys_app/request/token_interceptors.dart';

class DioUtil {
  // 连接超时时间
  static const int connectTimeout = 6000;

  // 响应超时时间
  static const int receiveTimeout = 6000;

  static Dio _dio = Dio();

  Dio get dio => _dio;

  factory DioUtil() => _getInstance();

  static DioUtil get instance => _getInstance();
  static DioUtil? _instance;

  static DioUtil _getInstance() {
    _instance ??= DioUtil._internal();
    return _instance!;
  }

  DioUtil._internal() {
    BaseOptions options = BaseOptions(
      connectTimeout: const Duration(milliseconds: connectTimeout),
      receiveTimeout: const Duration(milliseconds: receiveTimeout),
      responseType: ResponseType.json,
    );

    _dio = Dio(options);

    _dio.transformer = CustomTransformer()..jsonDecodeCallback = parseJson;

    Dio tokenDio = Dio(options);

    tokenDio.interceptors.add(TokenInterceptors());

    _dio.interceptors.add(LogInterceptor(responseBody: false));
    _dio.interceptors.add(AuthInterceptor(tokenDio: tokenDio));
  }

  Map<String, dynamic> _parseAndDecode(String response) {
    return jsonDecode(response) as Map<String, dynamic>;
  }

  Future<Map<String, dynamic>> parseJson(String text) {
    return compute(_parseAndDecode, text);
  }

  Future<Response<T>> request<T>(
    String url, {
    Object? data,
    Map<String, dynamic>? queryParameters,
    CancelToken? cancelToken,
    Options? options,
    ProgressCallback? onSendProgress,
    ProgressCallback? onReceiveProgress,
  }) async {
    Response<T> response = await _dio.request<T>(url,
        data: data,
        queryParameters: queryParameters,
        cancelToken: cancelToken,
        options: options,
        onSendProgress: onSendProgress,
        onReceiveProgress: onReceiveProgress);

    return response;
  }
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值