业务场景:并发请求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});@overridevoid 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);}}@overridevoid onResponse(Response response, ResponseInterceptorHandler handler) {handler.next(response);}@overridevoid onError(DioException err, ErrorInterceptorHandler handler) {// TODO: implement onErrorsuper.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 {@overridevoid 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;}
}