Flutter Dio 拦截器在 onError 中没有按预期工作 - 应该在 401 上刷新令牌

问题描述 投票:0回答:1

所以,我为 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 时,这是日志中的流程:

  • DioError被捕获,进入拦截器的onError
  • 检查错误是否为 401 并刷新令牌
  • 加载令牌并再次调用初始 GET 函数并返回预期值

调用 dio POST 时(上面的 uploadImage 函数):

  • 如果响应是 401,它不会进入拦截器的 onError 但立即调用 ToastMessage 并向用户显示上传过程未完成(实际上没有)
  • 发生这种情况后,它会进入 onError 拦截器并刷新令牌

所以,我的问题可能是:

为什么在POST函数中响应码是401而在GET函数中调用DioError拦截器的onError却没有被调用?

更新:

当 401 是 uploadImage 函数的响应时,这是流程:

  • 它进入拦截器
  • 刷新令牌
  • 令牌刷新成功后,它进入 try 块并使用正确的请求选项再次尝试调用 uploadImage
  • 突然跳回到 onError 拦截器的顶部(这意味着 try 块没有通过,尽管我没有收到任何类型的错误)
  • 返回 uploadImage 的 DioError 并返回 ToastMessage
flutter post http-status-code-401 refresh-token dio
1个回答
0
投票

我不确定,但我刚刚检查了我在 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);
    }
© www.soinside.com 2019 - 2024. All rights reserved.