Дарт http: «Плохое состояние: не удается завершить завершенный запрос» при повторной попытке http.Request после получения нового токена доступа - PullRequest
0 голосов
/ 29 июня 2018

В настоящее время я пытаюсь получить доступ к веб-API во Flutter, для которого требуется авторизационный токен JWT. Срок действия токена истекает через определенное время.

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

У меня проблемы с этим последним шагом. Кажется, что http.BaseRequest можно отправить только один раз. Как мне повторить запрос http с новым токеном?


Как указано в http-файле readme , я создал подкласс http.BaseClient для добавления режима авторизации. Вот упрощенная версия:

import 'dart:async';

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

class AuthorizedClient extends http.BaseClient {
  AuthorizedClient(this._authService) : _inner = http.Client();

  final http.Client _inner;
  final AuthService _authService;

  Future<http.StreamedResponse> send(http.BaseRequest request) async {
    final token = await _authService.getAccessToken();
    request.headers['Authorization'] = 'Bearer $token';

    final response = await _inner.send(request);

    if (response.statusCode == 401) {
      final newToken = await _authService.refreshAccessToken();
      request.headers['Authorization'] = 'Bearer $newToken';

      // throws error: Bad state: Can't finalize a finalized Request
      final retryResponse = await _inner.send(request);

      return retryResponse;
    }

    return response;
  }
}

abstract class AuthService {
  Future<String> getAccessToken();
  Future<String> refreshAccessToken();
}

Ответы [ 2 ]

0 голосов
/ 29 июня 2018

Вот то, что я до сих пор придумал, основываясь на ответе Ричарда Хипа: чтобы отправить запрос заново, мы должны его скопировать.

Пока я не смог найти решение для потоковых запросов!

http.BaseRequest _copyRequest(http.BaseRequest request) {
  http.BaseRequest requestCopy;

  if(request is http.Request) {
    requestCopy = http.Request(request.method, request.url)
      ..encoding = request.encoding
      ..bodyBytes = request.bodyBytes;
  }
  else if(request is http.MultipartRequest) {
    requestCopy = http.MultipartRequest(request.method, request.url)
      ..fields.addAll(request.fields)
      ..files.addAll(request.files);
  }
  else if(request is http.StreamedRequest) {
    throw Exception('copying streamed requests is not supported');
  }
  else {
    throw Exception('request type is unknown, cannot copy');
  }

  requestCopy
    ..persistentConnection = request.persistentConnection
    ..followRedirects = request.followRedirects
    ..maxRedirects = request.maxRedirects
    ..headers.addAll(request.headers);

  return requestCopy;
}
0 голосов
/ 29 июня 2018

Вы не можете send одно и то же BaseRequest дважды. Создайте новый BaseRequest из первого и отправьте эту копию.

Вот некоторый код (из io_client) для клонирования BaseRequest.

  var copyRequest = await _inner.openUrl(request.method, request.url);

  copyRequest
      ..followRedirects = request.followRedirects
      ..maxRedirects = request.maxRedirects
      ..contentLength = request.contentLength == null
          ? -1
          : request.contentLength
      ..persistentConnection = request.persistentConnection;
  request.headers.forEach((name, value) {
    copyRequest.headers.set(name, value);
  });
...