业务场景:并发请求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;
}
}