问题
I'm currently trying to access a Web API in Flutter that requires a JWT access token for authorization. The access token expires after a certain amount of time.
A new access token can be requested with a separate refresh token. Right now this access token refresh is performed as soon as a request returns a 401 response. After that, the failed request should be retried with the new access token.
I'm having trouble with this last step. It seems like a http.BaseRequest can only be sent once. How would I retry the http request with the new token?
As suggested in the dart http readme, I created a subclass of http.BaseClient to add the authorization behavior. Here is a simplified version:
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();
}
回答1:
You can't send the same BaseRequest twice. Make a new BaseRequest from the first one, and send that copy.
Here's some code (from io_client) to 'clone' a 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);
});
回答2:
Here is what I came up with so far, based on Richard Heap's answer: To resend a request, we have to copy it.
So far I was not able to come up for a solution for stream requests!
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;
}
来源:https://stackoverflow.com/questions/51096991/dart-http-bad-state-cant-finalize-a-finalized-request-when-retrying-a-http