7.2 读取用户资料&缓存

7.2 读取用户资料&缓存

实现步骤:


第 1 步:常量定义

lib/common/values/constants.dart

1
2
3
4
5
6
/// 常量
class Constants {
...
static const storageToken = 'token';// 登录成功后 token
static const storageProfile = 'profile';// 用户资料缓存
}

storageToken 登录成功后 token

storageProfile 用户资料缓存

第 2 步:登录请求 model

lib/common/models/woo/request/user_login.dart

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/// 用户登录
class UserLoginReq {
String? username;
String? password;

UserLoginReq({
this.username,
this.password,
});

factory UserLoginReq.fromJson(Map<String, dynamic> json) {
return UserLoginReq(
username: json['username'] as String?,
password: json['password'] as String?,
);
}

Map<String, dynamic> toJson() => {
'username': username,
'password': password,
};
}

第 3 步:登录 token 模型

lib/common/models/user_token.dart

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/// 用户令牌
class UserTokenModel {
String? token;

UserTokenModel({
this.token,
});

factory UserTokenModel.fromJson(Map<String, dynamic> json) {
return UserTokenModel(
token: json['token'] as String?,
);
}

Map<String, dynamic> toJson() => {
'token': token,
};
}

第 4 步:用户资料 model

直接复制返回 json

img

生成modellib/common/models/woo/user_profile_model

image-20220717231302757

用不同的目录进行区分, request response 的模型

第 5 步:api 编写

lib/common/api/user.dart

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/// 用户 api
class UserApi {
...

/// 登录
static Future<UserTokenModel> login(UserLoginReq? req) async {
var res = await WPHttpService.to.post(
'/users/login',
data: req,
);
return UserTokenModel.fromJson(res.data);
}

/// Profile
static Future<UserProfileModel> profile() async {
var res = await WPHttpService.to.get(
'/users/me',
);
return UserProfileModel.fromJson(res.data);
}
}

第 6 步:用户服务类

lib/common/services/user.dart

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
import 'dart:convert';
import 'package:get/get.dart';

import '../index.dart';

/// 用户服务
class UserService extends GetxService {
static UserService get to => Get.find();

final _isLogin = false.obs;
String token = '';
final _profile = UserProfileModel().obs;

/// 是否登录
bool get isLogin => _isLogin.value;

/// 用户 profile
UserProfileModel get profile => _profile.value;

/// 是否有令牌 token
bool get hasToken => token.isNotEmpty;

@override
void onInit() {
super.onInit();
// 读 token
token = Storage().getString(Constants.storageToken);
// 读 profile
var profileOffline = Storage().getString(Constants.storageProfile);
if (profileOffline.isNotEmpty) {
_profile(UserProfileModel.fromJson(jsonDecode(profileOffline)));
}
}

/// 设置令牌
Future<void> setToken(String value) async {
await Storage().setString(Constants.storageToken, value);
token = value;
}

/// 获取用户 profile
Future<void> getProfile() async {
if (token.isEmpty) return;
UserProfileModel result = await UserApi.profile();
_profile(result);
_isLogin.value = true;
Storage().setString(Constants.storageProfile, jsonEncode(result));
}

/// 设置用户 profile
Future<void> setProfile(UserProfileModel profile) async {
if (token.isEmpty) return;
_isLogin.value = true;
_profile(profile);
Storage().setString(Constants.storageProfile, jsonEncode(profile));
}

/// 注销
Future<void> logout() async {
// if (_isLogin.value) await UserAPIs.logout();
await Storage().remove(Constants.storageToken);
_profile(UserProfileModel());
_isLogin.value = false;
token = '';
}

/// 检查是否登录
Future<bool> checkIsLogin() async {
if (_isLogin.value == false) {
await Get.toNamed(RouteNames.systemLogin);
return false;
}
return true;
}
}

第 7 步:注册 UserService 服务

lib/global.dart

1
Get.put<UserService>(UserService());

第 8 步:登录请求

lib/pages/system/login/controller.dart

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/// Sign In
Future<void> onSignIn() async {
if ((formKey.currentState as FormState).validate()) {
try {
Loading.show();

// api 请求
UserTokenModel res = await UserApi.login(UserLoginReq(
username: userNameController.text,
password: passwordController.text,
));

// 本地保存 token
await UserService.to.setToken(res.token!);
// 获取用户资料
await UserService.to.getProfile();

Loading.success();
Get.back(result: true);
} finally {
Loading.dismiss();
}
}
}

第 9 步:HTTP 头加入 token 认证

lib/common/services/wp_http.dart

1
2
3
4
5
6
7
8
9
10
11
12
13
/// 拦截
class RequestInterceptors extends Interceptor {
@override
void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
// super.onRequest(options, handler);

// http header 头加入 Authorization
if (UserService.to.hasToken) {
options.headers['Authorization'] = 'Bearer ${UserService.to.token}';
}

return handler.next(options);
}

第 10 步:401 错误处理

lib/common/services/wp_http.dart

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/// 退出并重新登录
Future<void> _errorNoAuthLogout() async {
await UserService.to.logout();
Get.offAndToNamed(RouteNames.systemLogin);
}

@override
Future<void> onError(DioError err, ErrorInterceptorHandler handler) async {
final exception = HttpException(err.message);
switch (err.type) {
case DioErrorType.response: // 服务端自定义错误体处理
{
final response = err.response;
final errorMessage = ErrorMessage.fromJson(response?.data);
switch (errorMessage.statusCode) {
// 401 未登录
case 401:
// 注销 并跳转到登录页面
_errorNoAuthLogout();
break;
...

提交代码到 git