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 refreshToken() async {
    debugPrint('刷新token开始');
    String mpLoginURL = '${Api.baseUrl}/user/mpLogin';
    Map 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> _retry(RequestOptions requestOptions) async {
    return tokenDio.request(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 _parseAndDecode(String response) {
    return jsonDecode(response) as Map;
  }

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

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

    return response;
  }
}

你可能感兴趣的:(flutter,前端,dio)