[英]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.