简体   繁体   中英

Flutter: How to Refresh token when token expires during http call?

I am new to flutter and I am using http package for network call. I want to refresh token and call the request again if the response code of the request 200. How can I acheive this using http package? I heard about dio package but it is complicated for me.

User getUser(){

   final response = http.post(Uri.https(BASE_URL, '/api/user'), 
    headers: {'Authorization: Bearer $token'});

    if(response.statusCode == 200){
       return User.fromJson(jsonDecode(response.body)['user']);
    }
    else if(response.statusCode == 401){
      //refresh token and call getUser again     
    }

}

You can use refresh token as follows:

User getUser(){

   final response = http.post(Uri.https(BASE_URL, '/api/user'), 
    headers: {'Authorization: Bearer $token'});

    if(response.statusCode == 200){
       return User.fromJson(jsonDecode(response.body)['user']);
    }
    else if(response.statusCode == 401){
      //refresh token and call getUser again
      final response = http.post(Uri.https(BASE_URL, '/api/[YourAuthorizationEndpoint]'), 
    headers: {'grant_type': 'refresh_token', 'refresh_token': '$refresh_token'});
      token = jsonDecode(response.body)['token'];
      refresh_token = jsonDecode(response.body)['refresh_token'];
      return getUser();
    }

}

You can use dart's http/retry package:

import 'package:http/http.dart' as http;
import 'package:http_retry/http_retry.dart';

final client = RetryClient(
   http.Client(), 
   retries: 1,
   when: (response) {
    return response.statusCode == 401 ? true : false;
   },
   onRetry: (req, res, retryCount) {
     if (retryCount == 0 && res?.statusCode == 401) {
        // refresh token
     }  
   },
);

try {
  final response = await client.get('http://www.example.com');
} finally {
  client.close();
}

You can use Dio Package and create a Client Class and manage all the requests from there. Here I have done with the DIO package.

 class DioClient {
  final String baseUrl;

  Dio _dio = new Dio();

  DioClient(
    this.baseUrl,
  ) {
    _dio
      ..options.baseUrl = baseUrl
      ..options.connectTimeout = _defaultConnectTimeout
      ..options.receiveTimeout = _defaultReceiveTimeout
      ..httpClientAdapter
      ..options.responseType = ResponseType.json

    if (kDebugMode) {
      _dio.interceptors.add(LogInterceptor(
          responseBody: true,
          error: true,
          requestHeader: false,
          responseHeader: false,
          request: false,
          requestBody: false));
    }
  }

  Future<Response> get(
    String uri, {
    Map<String, dynamic> queryParameters,
    Options options,
    CancelToken cancelToken,
    ProgressCallback onReceiveProgress,
  }) async {
    try {
      var response = await _dio.get(
        uri,
        queryParameters: queryParameters,
        options: options,
        cancelToken: cancelToken,
        onReceiveProgress: onReceiveProgress,
      );
      return response;
    } on SocketException catch (e) {
      throw SocketException(e.toString());
    } on FormatException catch (_) {
      throw FormatException("Unable to process the data");
    } catch (error) {
      if (error is DioError) {
        switch (error.response.statusCode) {
          case 401:
            return await DioConnectivityRetrier(
                    dio: _dio, requestOptions: error.request)
                .requestRetrier();
            break;
          default:
            throw error;
            break;
        }
      } else {
        throw error;
      }
    }
  }

  Future<dynamic> post(
    String uri, {
    data,
    Map<String, dynamic> queryParameters,
    Options options,
    CancelToken cancelToken,
    ProgressCallback onSendProgress,
    ProgressCallback onReceiveProgress,
  }) async {
    try {
      var response = await _dio.post(
        uri,
        data: data,
        queryParameters: queryParameters,
        options: options,
        cancelToken: cancelToken,
        onSendProgress: onSendProgress,
        onReceiveProgress: onReceiveProgress,
      );
      return response;
    } on FormatException catch (_) {
      throw FormatException("Unable to process the data");
    } catch (error) {
      if (error is DioError) {
        switch (error.response.statusCode) {
          case 401:
            return await DioConnectivityRetrier(
                    dio: _dio, requestOptions: error.request)
                .requestRetrier();
            break;
          default:
            throw error;
            break;
        }
      } else {
        throw error;
      }
      throw error;
    }
  }

  Future<Response> delete(
    String uri, {
    Map<String, dynamic> queryParameters,
    Map<String, dynamic> data,
    Options options,
    CancelToken cancelToken,
    ProgressCallback onReceiveProgress,
  }) async {
    try {
      var response = await _dio.delete(uri,
          queryParameters: queryParameters,
          options: options,
          cancelToken: cancelToken,
          data: data);
      return response;
    } on SocketException catch (e) {
      throw SocketException(e.toString());
    } on FormatException catch (_) {
      throw FormatException("Unable to process the data");
    } catch (error) {
      if (error is DioError) {
        switch (error.response.statusCode) {
          case 401:
            return await DioConnectivityRetrier(
                    dio: _dio, requestOptions: error.request)
                .requestRetrier();
            break;
          default:
            throw error;
            break;
        }
      } else {
        throw error;
      }
    }
  }
}

Here is DioConnectivityRetrier class so whenever you get 401 status from your API our request call this class. And we can use the same request data to call that same API and get the data. Here

 class DioConnectivityRetrier {

  final RequestOptions requestOptions;
  final Dio dio;
  BuildContext context;
  DioConnectivityRetrier({this.requestOptions, this.dio,this.context});

  Future<Response> requestRetrier() async {

    print('*** request retrier called');
    await APIService()
        .refreshToken()
        .catchError((error) => throw error);

    requestOptions.headers = {
      'Authorization': 'Bearer ${UserAccess.token}',
    };

    print('new token->' + UserAccess.token);
    try {
      return await dio.request(requestOptions.path,
          options: requestOptions,
          data: requestOptions.data,
          cancelToken: requestOptions.cancelToken,
          onReceiveProgress: requestOptions.onReceiveProgress,
          onSendProgress: requestOptions.onReceiveProgress,
          queryParameters: requestOptions.queryParameters);
    } catch (e) {
      throw NetworkExceptions.getDioException(e);
    }
  }
}

And you can use it like this

  DioClient _dioClient = new DioClient(AppConstant.baseUrl);

 Response response = await _dioClient.post(apiUrl, data: data).catchError((error) => throw error);

call an api to refresh token after expiration

you can use this [package][1]

[1]: https://pub.dev/packages/jwt_decoder to check if token is expired

you can also get the time since token created and date of expiration

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM