Flutter develops the Dio interceptor to realize the function of token verification expiration

Keywords: Mobile network Android Retrofit Gradle

Preface:

Before, I shared the solution of using Retrofit to refresh token invalidation in Android. Now the Flutter project also has the requirement of "token verification is overdue". So I will briefly summarize how to implement the interceptor function of automatically refreshing token and resending request in the Flutter project, hoping to help you.

Ideas:

1.Dio implements network request

2. Customize the token interceptor to automatically refresh the token and resend the request

3.EventBus sends the login failure event, and the login page pops up

Implementation steps:

1. Configure the gradle dependency of Android directory

dependencies {
    ...
    implementation 'de.greenrobot:eventbus:3.0.0-beta1'
}

2. Add sdk in pubspec.yaml

dependencies:
  ...
  cupertino_icons: ^0.1.0
  dio: ^2.1.2
  event_bus: ^1.0.0

3. Encapsulate a new access token method

 Future<String> getToken() async {
    String accessToken = DataUtil.getAccessToken; //Get the current accessToken 
    String refreshToken = DataUtil.getRefreshToken; //Get the current refreshToken

    
    Dio dio =DioUtil.getInstance().tokenDio; ////Create a new Dio instance

    dio.options.headers['x-access-token'] = accessToken;//Set the current accessToken 

    try {
      String url = url; //refreshToken url
      var response = await dio.get(url,options: options); //Interface requesting refreshToken refresh
      accessToken = response.data['access_token']; //New accessToken
      refreshToken = response.data['refresh_token'];//New refreshToken
      DataUtil.saveRefreshToken(refreshToken); //Save the new refreshToken
    } on DioError catch (e) {
      if (e.response == null) {
      } else {
        eventBus.fire(new LoginEvent("Login"));//refreshToken expires, eventBus pops up the login page
      }
    }
    return accessToken;
  }

4. When the token fails, access token is obtained asynchronously

onError(DioError error) async {
    if (error.response != null && error.response.statusCode == 401) { 401 representative token Be overdue
      Dio dio = DioUtil().dio;//Get Dio singleton
      dio.lock();
      String accessToken = await getToken(); //Get new accessToken asynchronously
      DataUtil.saveAccessToken(accessToken); //Save new accessToken
      dio.unlock();
    }
    super.onError(error);
  }

5. Restart a request to get data

      //Reissue a request to get data
      var request = error.response.request;
      try {
        var response = await dio.request(request.path,
            data: request.data,
            queryParameters: request.queryParameters,
            cancelToken: request.cancelToken,
            options: request,
            onReceiveProgress: request.onReceiveProgress);
        return response;
      } on DioError catch (e) {
        return e;
     }

6. Complete code of DIO interceptor

class TokenInterceptor extends Interceptor {
  @override
  onError(DioError error) async {
    if (error.response != null && error.response.statusCode == 401) { //401 for token expiration
      Dio dio = DioUtil().dio;//Get Dio singleton
      dio.lock();
      String accessToken = await getToken(); //Get new accessToken asynchronously
      DataUtil.saveAccessToken(accessToken); //Save new accessToken
      dio.unlock();
      
      //Reissue a request to get data
      var request = error.response.request;
      try {
        var response = await dio.request(request.path,
            data: request.data,
            queryParameters: request.queryParameters,
            cancelToken: request.cancelToken,
            options: request,
            onReceiveProgress: request.onReceiveProgress);
        return response;
      } on DioError catch (e) {
        return e;
      }
    }
    super.onError(error);
  }


  Future<String> getToken() async {
    String accessToken = DataUtil.getAccessToken; //Get the current accessToken 
    String refreshToken = DataUtil.getRefreshToken; //Get the current refreshToken

    
    Dio dio =DioUtil.getInstance().tokenDio; ////Create a new Dio instance

    dio.options.headers['x-access-token'] = accessToken;//Set the current accessToken 

    try {
      String url = url; //refreshToken url
      var response = await dio.get(url,options: options); //Interface requesting refreshToken refresh
      accessToken = response.data['access_token']; //New accessToken
      refreshToken = response.data['refresh_token'];//New refreshToken
      DataUtil.saveRefreshToken(refreshToken); //Save the new refreshToken
    } on DioError catch (e) {
      if (e.response == null) {
      } else {
        eventBus.fire(new LoginEvent("Login"));//refreshToken expires, eventBus pops up the login page
      }
    }
    return accessToken;
  }

}

7. Add a custom token interceptor

/*
 *Dio Tool class of network request
 */
class DioUtil {
  Dio dio;
  Dio tokenDio = new Dio();
  static DioUtil _instance;

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

  //get method
  Future<Response> get(url, {data, options, cancelToken}) async {
   String accessToken = DataUtil.getAccessToken; //Get the current accessToken 
   String refreshToken = DataUtil.getRefreshToken; //Get the current refreshToken

    options = BaseOptions(
      connectTimeout: 15000,
      headers: {},
    );

    dio = new Dio(options);

    //Add a custom token interceptor
    dio.interceptors.add(new TokenInterceptor());

    Response response;
    try {
      response = await dio.get(url, cancelToken: cancelToken);
    } on DioError catch (e) {
      print(e.response.data);
    }
    return response;
  }

}

8.EventBus sends the login failure event, and the login page pops up

EventBus eventBus = new EventBus();

//eventBus class for custom login
class LoginEvent {
  String text;
  LoginEvent(this.text);
}


//Accept eventBus events on the logged in page
class LoginPageState extends State<LoginPage> {
  ....
  @override
  void initState() {
    super.initState();
    eventBus.on<LoginEvent>().listen((LoginEvent data) {
    //TO DO SOMETHING
    });
  } 

}

9. summary

In the Flutter project, a Dio interceptor that automatically refreshes and resends requests is customized. After constant debugging, the function is finally realized. If you have any questions, please contact me!

Posted by implications on Mon, 25 Nov 2019 09:23:33 -0800