3

I have declared a class to make api requests using flutter Dio as follows.

class DioUtil {
  static Dio _instance;

  static Dio getInstance() {
    if (_instance == null) {
      _instance = createDio();
    }
    return _instance;
  }

  static Dio createDio() {
    var dio = Dio();
    dio.interceptors.add(InterceptorsWrapper(onRequest: (options, handler) {
      // Do something before request is sent
      return handler.next(options); //continue
    }, onResponse: (response, handler) {
      // Do something with response data
      return handler.next(response); // continue
    }, onError: (DioError e, handler) async {
      if (e.response != null) {
        if (e.response.statusCode == 401) {
          var dio = DioUtil.getInstance();
          dio.interceptors.requestLock.lock();
          dio.interceptors.responseLock.lock();
          RequestOptions requestOptions = e.requestOptions;

          await refreshToken();
          Repository repository = Repository();
          var accessToken = await repository.readData("accessToken");
          final opts = new Options(
            method: requestOptions.method
          );
          dio.options.headers["Authorization"] = "Bearer " + accessToken;
          dio.interceptors.requestLock.unlock();
          dio.interceptors.responseLock.unlock();
          dio.request(requestOptions.path,
              options: opts,
              data: requestOptions.data,
              queryParameters: requestOptions.queryParameters);
        }//TODO: handle else clause
      }
    }));
    return dio;
  }

  static refreshToken() async {
    Response response;
    Repository repository = Repository();
    var dio = Dio();
    final Uri apiUrl = Uri.parse(BASE_PATH + "auth/reIssueAccessToken");
    var refreshToken = await repository.readData("refreshToken");
    dio.options.headers["Authorization"] = "Bearer " + refreshToken;
    response = await dio.postUri(apiUrl);
    if (response.statusCode == 200) {
      LoginResponse loginResponse =
          LoginResponse.fromJson(jsonDecode(response.toString()));
      repository.addValue('accessToken', loginResponse.data.accessToken);
      repository.addValue('refreshToken', loginResponse.data.refreshToken);
    } else {
      print(response.toString());
    }
  }
}

and I use flutter bloc pattern and my bloc is as follows.

class OurClassBloc extends Bloc<OurClassEvent, OurClassState> {
  OurClassBloc(OurClassState initialState) : super(initialState);
  Repository repository = Repository();

  @override
  Stream<OurClassState> mapEventToState(
    OurClassEvent event,
  ) async* {
    if (event is GetClasses) {
      yield* _getClassCategories(event);
    }
  }

  Stream<OurClassState> _getClassCategories(GetClasses event) async* {
    Response response;
    var dio = DioUtil.getInstance();
    final String apiUrl = (BASE_PATH + "classCategories");
    var accessToken = await repository.readData("accessToken");
    Map<String, dynamic> map = {"active": event.active};
    dio.options.headers["Authorization"] = "Bearer " + accessToken;
    dio.options.headers["Accept"] = "*/*";
    try {
      response = await dio.get(apiUrl, queryParameters: map);
      if (response.statusCode == 200) {
        OurClassResponse loginResponse =
            OurClassResponse.fromJson(jsonDecode(response.toString()));
        yield OurClassSuccess(loginResponse);
      }
      if (response.statusCode >= 400) {
        yield OurClassFailed();
      }
    } catch (e) {
      yield OurClassFailed();
    }
  }
}

When I make the requests with valid access token, I get 200 status code in bloc class and api works fine.when the token is expired, the dio class correctly gets the new token, make the same api call with new token successfully and inside the below callback I get the correct response also.

onResponse: (response, handler) {
  return handler.next(response);
}

but response doesn't comes to bloc class. Though it returned the response by calling return handler.next(response);,it is not coming to response variable inside _getClassCategories method.I expect the correct response should come to the response variable in bloc class for both scenarios:

  1. makes the api call with valid token.
  2. makes the api call with expired token.

but only scenario 1 is working in my code and hope someone here can help me to fix this.

EDIT- this works fine with dio previous version(3.0.10) - code

Krishan Madushanka
  • 319
  • 1
  • 5
  • 21

1 Answers1

1
          dio.request(requestOptions.path,
              options: opts,
              data: requestOptions.data,
              queryParameters: requestOptions.queryParameters);

This line creates a new request with no relation to the original one. If the request succeeds, there is no code listening for a response. If you want the original caller to receive anything, you will need to forward the response to the original handler:

          try {
              final response = await dio.request(requestOptions.path,
                  options: opts,
                  data: requestOptions.data,
                  queryParameters: requestOptions.queryParameters);
              handler.resolve(response);
          } on DioError catch (error) {
              handler.next(error); // or handler.reject(error);
          }

Also, be sure to forward the error to the handler in non-401 cases as well. Dio 4.0.0 interceptors don't automatically forward anything.

Nitrodon
  • 3,089
  • 1
  • 8
  • 15
  • wrote a full article on this. https://krishanmadushankadev.medium.com/how-to-handle-401-unauthorised-with-dio-interceptor-flutter-60398a914406 – Krishan Madushanka Feb 26 '22 at 05:54