flutter Dart http:“错误状态:获取新的访问令牌后重试http.Request时无法完成已完成的请求

vshtjzan  于 2023-03-13  发布在  Flutter
关注(0)|答案(3)|浏览(189)

我目前正在Flutter中尝试访问一个需要JWT访问令牌授权的Web API,访问令牌会在一段时间后过期。
可以使用单独的刷新令牌请求新的访问令牌。现在,只要请求返回401响应,就会执行此访问令牌刷新。之后,应使用新的访问令牌重试失败的请求
我在最后一步遇到了麻烦。看起来http.BaseRequest只能发送一次。我如何使用新令牌重试http请求?
正如dart http自述文件中所建议的,我创建了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();
}
mspsb9vt

mspsb9vt1#

以下是我根据理查德·希普的回答得出的结论:要重新发送请求,我们必须复制它。

到目前为止,我还无法提出流请求的解决方案!

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;
}
2q5ifsrm

2q5ifsrm2#

不能对同一个BaseRequest执行两次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);
  });
67up9zun

67up9zun3#

似乎发送相同的表单数据而不完成最后一个请求的问题,通过以下方式解决了相同的问题:

static final Dio dio2 = Dio();

  static Future<dynamic> _retry(RequestOptions requestOptions) async {
    final options = new Options(
      method: requestOptions.method,
      headers: (await getHeaders()),
    );

    options.responseType = (ResponseType.json);
    Map<String, String>? data;
    try {
      data = Map.fromEntries(requestOptions.data?.fields);
    } catch (e) {}
    var res = dio2.request<dynamic>(
        requestOptions.baseUrl + requestOptions.path,
        data: data == null ? null : FormData.fromMap(data),
        queryParameters: requestOptions.queryParameters,
        options: options);

    return res;
  }

在401块中这样使用它:

handler.resolve(await _retry(response.requestOptions));

相关问题