[英]How to handle the authentication token and and GraphQL client with Riverpod?
我们正在使用GraphQL将我们的后端与我们的移动应用程序连接起来。 我们正在使用Riverpod来处理全局 state、依赖注入等。
我们正在使用提供程序来处理GraphQL 客户端,无需任何身份验证 header 首先,而用户未进行身份验证(这将处理未经身份验证的请求,如登录或注册),在用户通过身份验证后,提供令牌的令牌提供程序被更新并它必须更新作为应用程序每个存储库用户的客户端提供程序:
final StateProvider<String> tokenProvider =
StateProvider<String>((_) => '', name: "Token Provider");
final graphQLClientProvider = Provider<GraphQLClient>(
(ref) {
final String token = ref.watch(tokenProvider).state;
final Link _link = HttpLink(
'http://192.168.1.36:1337/graphql',
defaultHeaders: {
if (token != '') 'Authorization': 'Bearer $token',
});
return GraphQLClient(
cache: GraphQLCache(),
link: _link,
);
},
name: "GraphQL Client Provider",
);
这里也有问题:
[ +141 ms] E/flutter ( 8361): [ERROR:flutter/lib/ui/ui_dart_state.cc(199)] Unhandled Exception: Bad state: Tried to use AuthNotifier after `dispose` was called.
[ ] E/flutter ( 8361):
[ ] E/flutter ( 8361): Consider checking `mounted`.
[ ] E/flutter ( 8361):
[ ] E/flutter ( 8361): #0 StateNotifier._debugIsMounted.<anonymous closure> (package:state_notifier/state_notifier.dart:128:9)
[ ] E/flutter ( 8361): #1 StateNotifier._debugIsMounted (package:state_notifier/state_notifier.dart:135:6)
[ ] E/flutter ( 8361): #2 StateNotifier.state= (package:state_notifier/state_notifier.dart:155:12)
[ ] E/flutter ( 8361): #3 AuthNotifier.signIn (package:thesis_cancer/features/auth/application/auth.notifier.dart:84:7)
[ ] E/flutter ( 8361): <asynchronous suspension>
[ ] E/flutter ( 8361): #4 _LoginCardState._submit (package:flutter_login/src/widgets/auth_card.dart:503:15)
[ ] E/flutter ( 8361): <asynchronous suspension>
[ ] E/flutter ( 8361):
这意味着当更新令牌时,接收存储库的身份验证通知程序取决于提供的客户端被更新/更改(遵循链):
final authRepositoryProvider = Provider<AuthRepository>(
(ref) => GraphQLAuthRepository(client: ref.read(graphQLClientProvider)),
name: 'Auth Repository Provider',
);
final authNotifierProvider = StateNotifierProvider<AuthNotifier, AuthState>(
(ref) => AuthNotifier(
authRepository: ref.watch(authRepositoryProvider),
dataStore: ref.watch(dataStoreRepositoryProvider),
profileRepository: ref.watch(profileRepositoryProvider),
tokenController: ref.watch(tokenProvider.notifier),
userController: ref.watch(userEntityProvider.notifier),
),
name: "Authentication Notifier Provider",
);
在条件( if )处中断的通知 function:
Future<String?> signIn({
required String username,
required String password,
}) async {
try {
final Map<String, dynamic> rawUser = await authRepository.signIn(
identifier: username, password: password) as Map<String, dynamic>;
final User sessionUser = User.fromJson(rawUser);
if (sessionUser.confirmed != false) {
tokenController.state = sessionUser.token!; <-- Bug begins here
}
await dataStore.writeUserProfile(sessionUser);
userController.state = sessionUser;
state = const AuthState.loggedIn();
} on LogInFailureByBadRequest {
return "E-posta veya şifre geçersiz.";
} on LogInFailure catch (error) {
return error.toString();
}
}
在对我们打开的问题发表评论之后,我们将ProviderReference
添加到我们的通知程序(在此问题之后),但它不起作用:
AuthNotifier(
this._ref, {
required this.authRepository,
required this.profileRepository,
required this.dataStore,
required this.tokenController,
required this.userController,
}) : super(const AuthState.loading());
final ProviderReference _ref;
final ProfileRepository profileRepository;
final AuthRepository authRepository;
final DataStoreRepository dataStore;
final StateController<User?> userController;
final StateController<String> tokenController;
在这一点上,我们不明白这里有什么问题。
更新
我们在StateNotifier及其提供者中删除了此更改。
使用Riverpod处理授权令牌和GraphQL客户端的正确方法是什么?
谢谢你。
问题是authRepositoryProvider
:
final authRepositoryProvider = Provider<AuthRepository>(
(ref) => GraphQLAuthRepository(client: ref.read(graphQLClientProvider)),
name: 'Auth Repository Provider',
);
在这个片段中, authRepositoryProvider
使用的是没有令牌的GraphQLClient
。 更新令牌时, graphQLClientProvider
state 更新良好,但authRepositoryProvider
不是因为您使用ref.read
避免重建authRepositoryProvider
state。 这意味着authRepositoryProvider
知道 graphQLClientProvider 的已处置(和卸载) graphQLClientProvider
,这就是错误消息的原因。
有2个解决方案:
ref.watch
:final authRepositoryProvider = Provider<AuthRepository>(
(ref) => GraphQLAuthRepository(client: ref.watch(graphQLClientProvider)),
name: 'Auth Repository Provider',
);
ref.read
作为参数:final authRepositoryProvider = Provider<AuthRepository>(
(ref) => GraphQLAuthRepository(client: ref.read),
name: 'Auth Repository Provider',
);
class AuthRepository {
AuthRepository(this.read)
final Reader read;
GraphQLClient get client => read(graphQLClientProvider);
}
请注意使用read
和watch
,第一个不会对更改做出反应,但可以在任何地方使用,而第二个对提供程序的 state 更新做出反应,但只能在另一个提供程序构造函数或提供访问权限的小部件中使用watch
或ref.watch
取决于 Riverpod 的版本。
此外,您的代码中还有另一个问题:
final User sessionUser = User.fromJson(rawUser);
if (sessionUser.confirmed != false) {
// In this moment, the token is updated,
// then the QrahpQLClient is updated,
// then the AuthNotifier is recreated,
// i.e the current is disposed
// and now becomes the previous
tokenController.state = sessionUser.token!; <-- Bug begins here
}
await dataStore.writeUserProfile(sessionUser);
userController.state = sessionUser;
// In this moment you are updating the state of a disposed notifier
state = const AuthState.loggedIn();
为了提供解决方案,有必要定义哪些元素将重建AuthNotifer.state
而不是重新创建通知程序。
您必须考虑基于ref.read
访问另一个通知程序,并在 StateController 构造函数中手动创建订阅。
将来,当 Riverpod 1.0 发布时,使用ref.listen
可以轻松解决此问题,但现在您需要重写代码。 您可以在bloc-to-bloc
通信指南中获得灵感: https://bloclibrary.dev/#/architecture?id=bloc-to-bloc-communication
我认为错误出在您的 AuthNotifier 中:
if (sessionUser.confirmed != false) {
tokenController.state = sessionUser.token!; <-- you are updating a dependencie of your actual AuthNotifier object
}
await dataStore.writeUserProfile(sessionUser); <-- during the await a new object is created and the actual instance is disposed so this line will throw
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.