所以,我为 api 调用设置了一个拦截器。它看起来像这样:
class AuthorizationInterceptor extends Interceptor {
@override
void onRequest(
RequestOptions options, RequestInterceptorHandler handler) async {
if (options.headers.containsKey('requiresToken') &&
options.headers['requiresToken'] == false) {
options.headers.remove('requiresToken');
super.onRequest(options, handler);
} else {
String token = await SecureStorage.loadAccessToken();
options.headers['Authorization'] = 'Bearer $token';
// options.headers['Content-Type'] = 'application/json';
super.onRequest(options, handler);
}
}
@override
void onError(DioError err, ErrorInterceptorHandler handler) async {
if (err.response?.statusCode == 401) {
log('++++++ interceptor error ++++++');
if (await SecureStorage.loadAccessToken() == '') {
super.onError(err, handler);
return;
}
bool isTokenRefreshed = await AuthApi.refreshToken();
if (isTokenRefreshed) {
RequestOptions origin = err.response!.requestOptions;
String token = await SecureStorage.loadAccessToken();
origin.headers["Authorization"] = "Bearer $token";
try {
final Response response = await DioClient.request(
url: origin.path,
data: origin.data,
options: Options(
headers: origin.headers,
method: origin.method,
),
);
handler.resolve(response);
} catch (e) {
super.onError(err, handler);
}
}
} else {
super.onError(err, handler);
return;
}
}
}
现在,当我使用 dio GET 方法调用一些 api 并且令牌已过期时,onError 拦截器处理 401 并刷新令牌。在那之后,之前调用的请求继续,一切都很好。
但是,当我尝试使用 dio POST 做正确的事情时,它并没有按预期工作。如果有 401 响应代码,它应该通过 onError 并刷新令牌,然后继续调用之前调用的 POST 函数,如下所示:
static Future uploadImage(PlatformFile image, String disclaimer,
{String? imageTitle}) async {
String imageExtension = image.extension!;
String imageName = '${imageTitle ?? 'image'}.$imageExtension';
final formData = FormData.fromMap({
'upload_file': MultipartFile.fromBytes(
image.bytes!,
filename: imageName,
contentType: MediaType('media_content', imageExtension),
),
'disclaimer': disclaimer,
});
try {
final response = await DioClient.post(
url: Endpoint.images,
data: formData,
options: Options(
headers: {
'Content-Type': 'multipart/form-data',
},
),
);
return response.data;
} on DioError catch (err) {
ToastMessage.apiError(err);
log('DioError uploadImage response: ${ToastMessage.message}');
}
}
这是我使用的许多其他功能之一,效果很好:
static Future getPosts(
{required int page,
int? pageSize,
String? searchParam,
String? status,
String? categoryId}) async {
try {
final response = await DioClient.get(
url: Endpoint.getPosts,
query: {
'page': page,
if (pageSize != null) 'page_size': pageSize,
if (status != null) 'status': status,
if (searchParam != null) 'search_param': searchParam,
if (categoryId != null) 'category_id': categoryId,
},
);
return response.data;
} on DioError catch (err) {
ToastMessage.apiError(err);
log('DioError get posts response: ${ToastMessage.message}');
}
}
到目前为止,我尝试了一切。我所做的一切看起来像这样:
当调用 dio GET 函数且响应为 401 时,这是日志中的流程:
调用 dio POST 时(上面的 uploadImage 函数):
所以,我的问题可能是:
为什么在POST函数中响应码是401而在GET函数中调用DioError拦截器的onError却没有被调用?
更新:
当 401 是 uploadImage 函数的响应时,这是流程:
我不确定,但我刚刚检查了我在 401 处理上的实现,我使用:
RequestOptions origin = err.requestOptions;
改为:
RequestOptions origin = err.response!.requestOptions;
这是我的代码的一部分
final Options newOptions = Options(
method: err.requestOptions.method,
headers: headers,
);
try {
final Response<dynamic> newResponse = await _dio.request(
err.requestOptions.path,
data: err.requestOptions.data,
queryParameters: err.requestOptions.queryParameters,
options: newOptions,
);
handler.resolve(newResponse);
} on DioError catch (err) {
handler.next(err);
}