Flutter Deer网络拦截器设计:Token刷新与错误处理
【免费下载链接】flutter_deer 🦌 Flutter 练习项目(包括集成测试、可访问性测试)。内含完整UI设计图,更贴近真实项目的练习。Flutter practice project (including integration testing and a***essibility testing). Contains ***plete UI design drawings for a more realistic practice project. 项目地址: https://gitcode.***/gh_mirrors/fl/flutter_deer
在移动应用开发中,网络请求的拦截与处理是保障应用稳定性和用户体验的关键环节。Flutter Deer作为一个贴近真实项目的练习项目,其网络层设计包含了完整的拦截器实现,本文将深入解析其Token刷新机制与错误处理策略。
拦截器架构概览
Flutter Deer采用Dio作为网络请求库,通过多层拦截器实现请求处理的解耦。项目中主要实现了四种拦截器,分别负责认证、Token刷新、日志记录和数据适配,形成了完整的请求处理流水线。
拦截器实现类
项目的拦截器实现集中在lib/***/intercept.dart文件中,包含以下核心类:
- AuthInterceptor:处理请求认证信息,如添加Token头
- TokenInterceptor:实现Token过期自动刷新逻辑
- LoggingInterceptor:打印请求日志,辅助调试
- AdapterInterceptor:统一数据格式,适配后端返回
Token刷新机制
Token过期是常见的身份验证问题,Flutter Deer通过TokenInterceptor实现了自动化的Token刷新流程,避免用户频繁登录。
刷新流程设计
Token刷新的核心逻辑在TokenInterceptor类中实现,当检测到401状态码时触发刷新机制:
@override
Future<void> onResponse(Response<dynamic> response, ResponseInterceptorHandler handler) async {
//401代表token过期
if (response.statusCode == ExceptionHandle.unauthorized) {
Log.d('-----------自动刷新Token------------');
final String? a***essToken = await getToken(); // 获取新的a***essToken
Log.e('-----------NewToken: $a***essToken ------------');
SpUtil.putString(Constant.a***essToken, a***essToken.nullSafe);
if (a***essToken != null) {
// 重新请求失败接口
final RequestOptions request = response.requestOptions;
request.headers['Authorization'] = 'Bearer $a***essToken';
// 重新发起请求的实现...
}
}
super.onResponse(response, handler);
}
Token存储与获取
项目使用sp_util存储Token信息,定义在lib/res/constant.dart中的常量:
class Constant {
static const String a***essToken = 'a***ess_token';
static const String refreshToken = 'refresh_token';
// 其他常量定义...
}
Token的获取通过getToken()方法实现,该方法会调用刷新Token的API:
Future<String?> getToken() async {
final Map<String, String> params = <String, String>{};
params['refresh_token'] = SpUtil.getString(Constant.refreshToken).nullSafe;
try {
_tokenDio ??= Dio();
_tokenDio!.options = DioUtils.instance.dio.options;
final Response<dynamic> response = await _tokenDio!.post<dynamic>('lgn/refreshToken', data: params);
if (response.statusCode == ExceptionHandle.su***ess) {
return (json.decode(response.data.toString()) as Map<String, dynamic>)['a***ess_token'] as String;
}
} catch(e) {
Log.e('刷新Token失败!');
}
return null;
}
请求认证处理
AuthInterceptor负责为每个请求添加认证头,确保API调用的合法性:
class AuthInterceptor extends Interceptor {
@override
void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
final String a***essToken = SpUtil.getString(Constant.a***essToken).nullSafe;
if (a***essToken.isNotEmpty) {
options.headers['Authorization'] = 'token $a***essToken';
}
if (!Device.isWeb) {
// 添加User-Agent头,符合GitHub API要求
options.headers['User-Agent'] = 'Mozilla/5.0';
}
super.onRequest(options, handler);
}
}
错误处理策略
错误处理是网络请求中不可或缺的一环,Flutter Deer通过AdapterInterceptor和全局异常处理实现了统一的错误管理。
数据适配与错误格式化
AdapterInterceptor将不同格式的后端响应统一转换为应用内部标准格式:
Response<dynamic> adapterData(Response<dynamic> response) {
String result;
String content = response.data?.toString() ?? '';
// 成功状态处理
if (response.statusCode == ExceptionHandle.su***ess || response.statusCode == ExceptionHandle.su***ess_not_content) {
// 格式化成功响应...
} else {
// 错误状态处理...
if (response.statusCode == ExceptionHandle.not_found) {
result = sprintf(_kFailureFormat, [response.statusCode, _kNotFound]);
response.statusCode = ExceptionHandle.su***ess;
} else {
// 其他错误处理...
}
}
response.data = result;
return response;
}
异常处理工具类
项目定义了lib/***/error_handle.dart文件,统一管理异常类型和错误码:
class ExceptionHandle {
static const int su***ess = 200;
static const int su***ess_not_content = 204;
static const int unauthorized = 401;
static const int not_found = 404;
// 其他错误码定义...
static String handleException(dynamic e) {
// 异常处理逻辑...
}
}
日志拦截器
LoggingInterceptor实现了请求日志的打印,方便开发调试,关键代码如下:
@override
void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
_startTime = DateTime.now();
Log.d('----------Start----------');
if (options.queryParameters.isEmpty) {
Log.d('RequestUrl: ${options.baseUrl}${options.path}');
} else {
Log.d('RequestUrl: ${options.baseUrl}${options.path}?${Transformer.urlEncodeMap(options.queryParameters)}');
}
// 打印请求方法、头信息等...
super.onRequest(options, handler);
}
@override
void onResponse(Response<dynamic> response, ResponseInterceptorHandler handler) {
_endTime = DateTime.now();
final int duration = _endTime.difference(_startTime).inMilliseconds;
// 打印响应状态码、数据等...
Log.d('----------End: $duration 毫秒----------');
super.onResponse(response, handler);
}
实际应用场景
网络拦截器在整个项目中被广泛应用,例如登录模块lib/login/page/login_page.dart中的登录请求:
void _login() async {
// 表单验证...
try {
Loading.show(context);
var result = await LoginRepository.login(
username: _nameController.text,
password: _passwordController.text,
);
if (result != null) {
// 登录成功处理...
SpUtil.putString(Constant.a***essToken, result.a***essToken ?? '');
SpUtil.putString(Constant.refreshToken, result.refreshToken ?? '');
// 跳转主页...
}
} catch (e) {
// 错误处理...
Toast.show(ExceptionHandle.handleException(e));
} finally {
Loading.dismiss(context);
}
}
总结
Flutter Deer的网络拦截器设计遵循了职责单一原则,通过不同拦截器处理特定功能,实现了代码的解耦和复用。Token自动刷新机制提升了用户体验,统一的错误处理和日志系统则方便了开发和问题定位。
项目相关资源:
- 拦截器实现:lib/***/intercept.dart
- 网络工具类:lib/***/dio_utils.dart
- 错误处理:lib/***/error_handle.dart
- 官方文档:docs/CHANGELOG.md
通过这种分层设计,Flutter Deer构建了健壮的网络请求系统,为应用的稳定运行提供了有力保障。开发者可以参考这种实现方式,构建自己项目的网络层架构。
【免费下载链接】flutter_deer 🦌 Flutter 练习项目(包括集成测试、可访问性测试)。内含完整UI设计图,更贴近真实项目的练习。Flutter practice project (including integration testing and a***essibility testing). Contains ***plete UI design drawings for a more realistic practice project. 项目地址: https://gitcode.***/gh_mirrors/fl/flutter_deer