简体   繁体   English

Flutter:在 http 调用期间令牌过期时如何刷新令牌?

[英]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.我是 flutter 的新手,我正在使用http package 进行网络呼叫。 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?如果请求的响应代码为 200,我想刷新令牌并再次调用请求。如何使用 http package 来实现这一点? I heard about dio package but it is complicated for me.我听说过dio package 但对我来说很复杂。

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:您可以使用 dart 的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.您可以使用Dio Package并创建一个客户端 Class 并从那里管理所有请求。 Here I have done with the DIO package.在这里,我已经完成了 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.这是DioConnectivityRetrier class,所以每当您从 API 获得 401 状态时,我们的请求都会调用此 class。 And we can use the same request data to call that same API and get the data.我们可以使用相同的请求数据调用相同的 API 并获取数据。 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到期后调用 api 刷新令牌

you can use this [package][1]你可以使用这个[包][1]

[1]: https://pub.dev/packages/jwt_decoder to check if token is expired [1]: https://pub.dev/packages/jwt_decoder检查令牌是否过期

you can also get the time since token created and date of expiration您还可以获得自令牌创建以来的时间和到期日期

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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