Flutter/Dart: вызвать функцию внутри себя, чтобы многократно запускать таймер для обновления токена JWT?

Вместо того, чтобы проверять, истек ли срок действия моего токена JWT для каждого запроса, я хотел бы проверять его только при первой инициализации приложения в main а затем автоматически обновлять его каждые 55 минут.

Вот моя функция обновления, которую я вызываю в верхней части дерева виджетов;

       void main() async {
    await refreshToken()
 };

А вот код refreshToken;

        Future<String?> refreshToken() async {

  String? refreshToken =  await getCryptRefresh(); //gets refresh token from an encrypted box
      final http.Response response = 
      await http.post(Uri.parse('http://example.com/refresh'),
         headers: <String, String>{'Content-Type': 'application/json; charset=UTF-8'},
         body: jsonEncode(<String?, dynamic>{
        'email': currentemail, 
        'id': currentuserid,
        'refreshToken': refreshToken
        }),
      );

    if (response.body.contains('token')) {
       Map<String, dynamic> refreshMap = json.decode(response.body);
         String token = refreshMap['token'];
           putCryptJWT(token); // stores new token in encrypted Hive box
               print("token renewed = " + token);
               
                 Timer(Duration(minutes: 55), () {
                     refreshToken();
                 });
          
               return token;
    } else {
            String noresponse = 'responsebody doesnt contain token';
                print(noresponse);
    }
}

Студия Android сначала выдала мне нулевую ошибку с красной линией под refreshToken()и предложил нулевую проверку !. Но как только я это сделал, я дал мне эту ошибку;

      The expression doesn't evaluate to a function, so it can't be invoked.

Было предложено удалить круглые скобки в вызове геттера. Поэтому я удалил круглые скобки и нулевую отметку, чтобы просто refreshToken;

Но он не запускается каждые 55 минут, как я надеялся.

Я бы предпочел не проверять срок действия по каждому запросу. И мой JWTToken, и его RefreshToken хранятся в зашифрованном ящике Hive. Следовательно, проверка каждого запроса кажется немного сложной.

3 ответа

Я бы не советовал так поступать. Вы создаете для себя много работы. Лучшим подходом было бы перехватить ответ на ваш запрос. Проверьте, является ли код ответа 401 (что означает несанкционированный), который, я думаю, вернет ваш бэкэнд. Затем обновите токен и снова запустите исходный запрос. Это гораздо более простой способ работы, поэтому нет ненужных проверок срока действия токенов, и пользовательский интерфейс по-прежнему остается безупречным. Вы можете легко сделать это с помощью пакета Dio. Вы можете сделать что-то подобное.

      var _http;
void initMethod(){
_http = Dio();
//set some headers
_http.options.contentType = "application/json";
_http.options.headers['Content-Type'] = "application/json";
//Now add interceptors for both request and response to add a token on outgoing request and to handle refresh on failed response.
_http.interceptors
        .add(InterceptorsWrapper(onRequest: (RequestOptions options) async {
      return await _addAuthenticationToken(options);
    }, onResponse: (Response response) async {
      if (response.statusCode == 401) return await refreshToken(response);
return response;
    }));
}

Future<Response> refreshtoken(Response response){
//Get your new token here
//Once you have the token, retry the original failed request. (After you set the token where ever you store it etc)
return await _retry(response);
}
Future<RequestOptions> _addAuthenticationToken(RequestOptions options) async {
    
    if (tokenPair != null)
      options.headers['Authorization'] =
          "Bearer " + "yout token goes here";
    return options;
  }

Future<Response<dynamic>> _retry(RequestOptions requestOptions) async {
    final options = new Options(
      method: requestOptions.method,
      headers: requestOptions.headers,
    );
    return await this._http.request<dynamic>(requestOptions.path,
        data: requestOptions.data,
        queryParameters: requestOptions.queryParameters,
        options: options);
  }

Возможно, вам придется поэкспериментировать, чтобы убедиться, что вы получили желаемый результат, но это должно помочь

В некоторых случаях вы не можете получить ответ от серверной части, как в моем случае. Я добавляю счетчик в приложение, так как начинаю получать токены, и продолжаю считать. Ваш способ - это ответ на тактовую частоту процессора, которую я тестировал, пока она не была точной, некоторые устройства могут считать быстрее, некоторые даже медленнее. Мой способ будет использовать дату и время сейчас и проверять другое время даты сейчас с меткой времени, которую мы сохраняем в начале каждые 15 секунд, поэтому она все еще не совсем точна, но, по крайней мере, ошибка не должна быть более 15 секунд.

      DateTime _timeoutAt;

start() {
   _timeoutAt = DateTime.now().add(Duration(minutes: 55);
   Timer _timerCheckIsTimeout = Timer.periodic(Duration(seconds: 15), (timer) {
   final isTimeout = DateTime.now().isAfter(_timeoutAt)
   if (isTimeout) {
      //Call back from here
      await refreshToken();
      timer.cancel();
   }
}

Я знаю, что это не лучшая практика, но у людей разные условия, и они не могут применить лучшую практику. Используйте приведенный выше пример, если вы можете управлять серверной частью, чтобы ответить на него.

Я не знаю, предпочтительнее ли это использовать Dio, но я просто сделал это для всех HTTP-запросов post;

      else if (response.body.contains('TokenExpiredError')) {
     await refreshToken();
       return downloadMainJSON(
        );

Вроде хорошо работает.

Другие вопросы по тегам