将 Dio 拦截器添加到 Flutter Riverpod
Add Dio Interceptor to Flutter Riverpod
我正在向服务器发出请求,但一段时间后 access token
过期。我正在使用 Dio,最近遇到了拦截器。如何向所有调用添加拦截器并在返回 401
时使用 refresh token
获取 access token
。
我将 tokens
存储在 Shared Preferences
中。这是我的代码。
class AuthenticationService {
final Dio _dio;
final LocalDBService _prefs;
AuthenticationService(this._dio, this._prefs);
Future<User?> fetchUser() async {
try {
Tokens _tokens = await _prefs.getTokens();
if (_tokens.accessToken == '') {
return null;
}
final result = await _dio.get(
CURRENT_USER,
options: Options(headers: {
"Authorization": "Bearer ${_tokens.accessToken}",
'Accept': "application/json",
}),
);
User _user = User.fromJson(result.data);
return _user;
} catch (e) {
print("fetcherror");
print(e);
return null;
}
}
}
final authenticationServiceProvider = Provider.autoDispose<AuthenticationService>((ref) {
final _prefs = ref.read(localDBProvider);
final _dio = Dio(); // Need to add interceptors to this
return AuthenticationService(_dio, _prefs);
});
final userProvider = FutureProvider.autoDispose<User?>((ref) {
ref.maintainState = true;
return ref.read(authenticationServiceProvider).fetchUser();
});
如何以及在何处添加拦截器。还想将 Dio()
实例移动到它自己的 class
我会推荐 2 个 dio 实例,一个用于日志记录 (authProvider),另一个用于应用程序的其余部分,因此您可以锁定一个并使用带有自己的 dio 实例的 authProvider 首先刷新令牌
class AuthenticatorInterceptor extends Interceptor {
final AuthenticationService authService;
final Dio dioReference;
AuthenticatorInterceptor(this.authService, this.dioReference);
@override
void onRequest(
RequestOptions options, RequestInterceptorHandler handler) async {
Tokens _tokens = await authService.token;
/// get the token from your authService and do some logic with it:
/// check if its still valid (expirationTime)
/// refresh it if its not, etc.
if (_token.refreshToken != null && _token.hasExpired) { // check somehow if the token is invalid and try to refresh it
try {
dioReference.lock(); // lock the current dio instance
authService.refresh(); //try to get a new access token
_tokens = await authService.token; //get the new token if the refresh was succesful
} on DioError catch (error) { // there was an error refreshing, report it or do some logic
dioReference.clear();
return handler.reject(error);
} finally {
dioReference.unlock(); //unlock the instance and move on
}
}
// if there was no error you can move on with your request with the new token (if any)
options.headers
.putIfAbsent(HttpHeaders.authorizationHeader, () => "Bearer ${_tokens.accessToken}");
return handler.next(options);
}
@override
void onError(DioError error, ErrorInterceptorHandler handler) async {
if (error.type == DioErrorType.response &&
error.response!.statusCode == 401) {
// If you still receive 401 then maybe you should logout and report the user that the refresh token is invalid (maybe the server removed it)
}
return super.onError(error, handler);
}
}
现在创建一个带有 Dio 实例的提供商,您将在您的应用程序中使用该实例(这与您将在 AuthService 中使用的实例不同,因此您可以在请求令牌时锁定它)
final dioProvider = Provider<Dio>((ref) {
final authenticationService = ref.watch(authenticationServiceProvider);
final Dio dio = Dio();
ref.onDispose(dio.close);
return dio
..interceptors.addAll([
AuthenticatorInterceptor(authenticationService, dio), //this interceptor holds a reference of your authService and the dio instance that uses it
]);
}, name: 'Dio');
然后像这样使用它
final userProvider = FutureProvider.autoDispose<User?>((ref) {
final dio = ref.watch(dioProvider);
final result = await dio.get(
CURRENT_USER, // I have no idea where you get this
);
User _user = User.fromJson(result.data);
ref.maintainState = true; // the request was succesful, you can mantain the state now
return _user;
});
当然后台还有很多逻辑要做,你的AuthService在做request之前至少要有一个token(如果你知道用户没有登录就没有调用的目的)但是这个应该给出如何处理拦截器的一般概念
我正在向服务器发出请求,但一段时间后 access token
过期。我正在使用 Dio,最近遇到了拦截器。如何向所有调用添加拦截器并在返回 401
时使用 refresh token
获取 access token
。
我将 tokens
存储在 Shared Preferences
中。这是我的代码。
class AuthenticationService {
final Dio _dio;
final LocalDBService _prefs;
AuthenticationService(this._dio, this._prefs);
Future<User?> fetchUser() async {
try {
Tokens _tokens = await _prefs.getTokens();
if (_tokens.accessToken == '') {
return null;
}
final result = await _dio.get(
CURRENT_USER,
options: Options(headers: {
"Authorization": "Bearer ${_tokens.accessToken}",
'Accept': "application/json",
}),
);
User _user = User.fromJson(result.data);
return _user;
} catch (e) {
print("fetcherror");
print(e);
return null;
}
}
}
final authenticationServiceProvider = Provider.autoDispose<AuthenticationService>((ref) {
final _prefs = ref.read(localDBProvider);
final _dio = Dio(); // Need to add interceptors to this
return AuthenticationService(_dio, _prefs);
});
final userProvider = FutureProvider.autoDispose<User?>((ref) {
ref.maintainState = true;
return ref.read(authenticationServiceProvider).fetchUser();
});
如何以及在何处添加拦截器。还想将 Dio()
实例移动到它自己的 class
我会推荐 2 个 dio 实例,一个用于日志记录 (authProvider),另一个用于应用程序的其余部分,因此您可以锁定一个并使用带有自己的 dio 实例的 authProvider 首先刷新令牌
class AuthenticatorInterceptor extends Interceptor {
final AuthenticationService authService;
final Dio dioReference;
AuthenticatorInterceptor(this.authService, this.dioReference);
@override
void onRequest(
RequestOptions options, RequestInterceptorHandler handler) async {
Tokens _tokens = await authService.token;
/// get the token from your authService and do some logic with it:
/// check if its still valid (expirationTime)
/// refresh it if its not, etc.
if (_token.refreshToken != null && _token.hasExpired) { // check somehow if the token is invalid and try to refresh it
try {
dioReference.lock(); // lock the current dio instance
authService.refresh(); //try to get a new access token
_tokens = await authService.token; //get the new token if the refresh was succesful
} on DioError catch (error) { // there was an error refreshing, report it or do some logic
dioReference.clear();
return handler.reject(error);
} finally {
dioReference.unlock(); //unlock the instance and move on
}
}
// if there was no error you can move on with your request with the new token (if any)
options.headers
.putIfAbsent(HttpHeaders.authorizationHeader, () => "Bearer ${_tokens.accessToken}");
return handler.next(options);
}
@override
void onError(DioError error, ErrorInterceptorHandler handler) async {
if (error.type == DioErrorType.response &&
error.response!.statusCode == 401) {
// If you still receive 401 then maybe you should logout and report the user that the refresh token is invalid (maybe the server removed it)
}
return super.onError(error, handler);
}
}
现在创建一个带有 Dio 实例的提供商,您将在您的应用程序中使用该实例(这与您将在 AuthService 中使用的实例不同,因此您可以在请求令牌时锁定它)
final dioProvider = Provider<Dio>((ref) {
final authenticationService = ref.watch(authenticationServiceProvider);
final Dio dio = Dio();
ref.onDispose(dio.close);
return dio
..interceptors.addAll([
AuthenticatorInterceptor(authenticationService, dio), //this interceptor holds a reference of your authService and the dio instance that uses it
]);
}, name: 'Dio');
然后像这样使用它
final userProvider = FutureProvider.autoDispose<User?>((ref) {
final dio = ref.watch(dioProvider);
final result = await dio.get(
CURRENT_USER, // I have no idea where you get this
);
User _user = User.fromJson(result.data);
ref.maintainState = true; // the request was succesful, you can mantain the state now
return _user;
});
当然后台还有很多逻辑要做,你的AuthService在做request之前至少要有一个token(如果你知道用户没有登录就没有调用的目的)但是这个应该给出如何处理拦截器的一般概念