Dart 异常处理全面指南
目录
1. 异常处理基础
1.1 基本语法
try-catch 语句
try {
var result = 10 ~/ 0; // 可能抛出异常的代码
print('结果: $result');
} catch (e) {
print('捕获异常: $e');
print('异常类型: ${e.runtimeType}');
}
捕获特定异常类型
try {
// 危险操作
validateInput(data);
} on ArgumentError catch (e) {
print('参数错误: ${e.message}');
} on FormatException catch (e) {
print('格式错误: ${e.message}');
} catch (e) {
print('其他异常: $e');
}
finally 块
try {
performOperation();
} catch (e) {
handleError(e);
} finally {
// 无论是否发生异常都会执行
cleanup();
}
1.2 抛出异常
抛出内置异常
void validateAge(int age) {
if (age < 0) {
throw ArgumentError.value(age, 'age', '年龄不能为负数');
}
if (age > 150) {
throw RangeError.value(age, 0, 150, 'age', '年龄必须在0-150之间');
}
}
重新抛出异常
void middleLayerOperation() {
try {
lowLevelOperation();
} catch (e) {
print('记录错误日志: $e');
rethrow; // 重新抛出原始异常
}
}
1.3 异常类型体系
Dart 内置异常类型
Exception: 基础异常接口Error: 程序错误基类ArgumentError: 参数错误RangeError: 范围错误StateError: 状态错误FormatException: 格式异常TimeoutException: 超时异常UnsupportedError: 不支持的操作
2. 自定义异常类
2.1 异常类层次结构
abstract class AppException implements Exception {
final String message;
final String code;
final DateTime timestamp;
final dynamic originalError;
const AppException(
this.message,
this.code, {
this.originalError,
DateTime? timestamp,
}) : timestamp = timestamp ?? DateTime.now();
@override
String toString() => '$runtimeType($code): $message';
Map<String, dynamic> toJson() => {
'type': runtimeType.toString(),
'code': code,
'message': message,
'timestamp': timestamp.toIso8601String(),
'originalError': originalError?.toString(),
};
}
2.2 具体异常类型
业务逻辑异常
class BusinessException extends AppException {
final String? field;
final dynamic invalidValue;
const BusinessException(
String message,
String code, {
this.field,
this.invalidValue,
dynamic originalError,
}) : super(message, code, originalError: originalError);
}
验证异常
class ValidationException extends BusinessException {
final List<String> errors;
const ValidationException(
String message,
this.errors, {
String? field,
dynamic invalidValue,
}) : super(
message,
'VALIDATION_ERROR',
field: field,
invalidValue: invalidValue,
);
}
网络异常
class NetworkException extends AppException {
final int? statusCode;
final String? url;
final Duration? timeout;
const NetworkException(
String message,
String code, {
this.statusCode,
this.url,
this.timeout,
dynamic originalError,
}) : super(message, code, originalError: originalError);
factory NetworkException.timeout(String url, Duration timeout) {
return NetworkException(
'网络请求超时',
'NETWORK_TIMEOUT',
url: url,
timeout: timeout,
);
}
}
2.3 异常处理策略
异常处理器接口
abstract class ExceptionHandler<T extends Exception> {
bool canHandle(Exception exception);
void handle(T exception);
}
异常管理器
class ExceptionManager {
final List<ExceptionHandler> _handlers = [];
void registerHandler(ExceptionHandler handler) {
_handlers.add(handler);
}
void handleException(Exception exception) {
for (var handler in _handlers) {
if (handler.canHandle(exception)) {
handler.handle(exception);
return;
}
}
_handleUnknownException(exception);
}
}
3. 异步异常处理
3.1 Future 异常处理
基本异步异常处理
// 使用 async/await
try {
var result = await performAsyncOperation();
print('成功: $result');
} catch (e) {
print('异步异常: $e');
}
// 使用 then/catchError
performAsyncOperation()
.then((value) => print('成功: $value'))
.catchError((error) => print('错误: $error'));
超时处理
try {
var result = await longRunningOperation()
.timeout(Duration(seconds: 30));
print('在超时前完成: $result');
} on TimeoutException catch (e) {
print('操作超时: ${e.message}');
}
// 带回退值的超时处理
var result = await longRunningOperation()
.timeout(
Duration(seconds: 10),
onTimeout: () => 'default_value',
);
并发异常处理
// 使用 Future.wait
try {
var results = await Future.wait([
task1(),
task2(),
task3(),
]);
print('所有任务完成: $results');
} catch (e) {
print('第一个异常: $e');
}
// 处理部分失败
var futures = [task1(), task2(), task3()];
var results = <String>[];
var errors = <String>[];
await Future.wait(futures.map((future) async {
try {
var result = await future;
results.add(result);
} catch (e) {
errors.add(e.toString());
}
}));
3.2 Stream 异常处理
基本 Stream 异常处理
stream.listen(
(data) => print('数据: $data'),
onError: (error) => print('错误: $error'),
onDone: () => print('完成'),
cancelOnError: false, // 错误时不取消订阅
);
await for 异常处理
try {
await for (var data in stream) {
print('处理数据: $data');
}
} catch (e) {
print('Stream 异常: $e');
}
Stream 转换异常处理
stream
.map((data) {
if (data == null) throw ArgumentError('数据不能为空');
return processData(data);
})
.handleError((error) {
print('转换错误: $error');
return 'error_value'; // 提供默认值
})
.listen((result) => print('结果: $result'));
3.3 Isolate 异常处理
基本 Isolate 异常处理
try {
var result = await Isolate.run(() {
return performHeavyComputation();
});
print('计算结果: $result');
} catch (e) {
print('Isolate 异常: $e');
}
复杂 Isolate 通信
var receivePort = ReceivePort();
var errorPort = ReceivePort();
var isolate = await Isolate.spawn(
isolateEntryPoint,
receivePort.sendPort,
onError: errorPort.sendPort,
);
receivePort.listen((message) {
print('收到消息: $message');
});
errorPort.listen((errorData) {
print('收到错误: $errorData');
});
4. 高级设计模式
4.1 分层异常处理
表示层(Controller)
class UserController {
void handleRequest(String action, Map<String, dynamic> data) {
try {
// 业务逻辑调用
_processRequest(action, data);
} on ValidationException catch (e) {
_sendErrorResponse('VALIDATION_ERROR', e.message, e.errors);
} on BusinessException catch (e) {
_sendErrorResponse(e.code, e.message);
} on SystemException catch (e) {
_sendErrorResponse('SYSTEM_ERROR', '系统暂时不可用');
_logSystemError(e);
} catch (e) {
_sendErrorResponse('UNKNOWN_ERROR', '未知错误');
_logUnknownError(e);
}
}
}
业务逻辑层(Service)
class UserService {
Map<String, dynamic> createUser(Map<String, dynamic> userData) {
try {
_validateUserData(userData);
return _repository.create(userData);
} on ValidationException {
rethrow; // 向上传播验证异常
} on DataAccessException catch (e) {
throw SystemException('用户创建失败', 'USER_CREATE_FAILED', originalError: e);
}
}
}
4.2 异常处理模式
结果类型模式
class Result<T, E> {
final T? _value;
final E? _error;
final bool _isSuccess;
factory Result.success(T value) => Result._(value, null, true);
factory Result.failure(E error) => Result._(null, error, false);
bool get isSuccess => _isSuccess;
T get value => _isSuccess ? _value! : throw StateError('结果为失败状态');
E get error => !_isSuccess ? _error! : throw StateError('结果为成功状态');
}
Future<Result<String, String>> performOperation() async {
try {
var result = await dangerousOperation();
return Result.success(result);
} catch (e) {
return Result.failure(e.toString());
}
}
重试模式
Future<T> retryWithBackoff<T>(
Future<T> Function() operation, {
required int maxRetries,
required Duration baseDelay,
}) async {
var currentRetry = 0;
while (currentRetry <= maxRetries) {
try {
return await operation();
} catch (e) {
currentRetry++;
if (currentRetry > maxRetries) rethrow;
var delay = Duration(
milliseconds: baseDelay.inMilliseconds * (1 << (currentRetry - 1)),
);
await Future.delayed(delay);
}
}
throw StateError('重试逻辑错误');
}
断路器模式
class CircuitBreaker {
final int _failureThreshold;
final Duration _recoveryTimeout;
int _failureCount = 0;
DateTime? _lastFailureTime;
bool _isOpen = false;
Future<T> execute<T>(Future<T> Function() operation) async {
if (_isOpen) {
if (_shouldAttemptReset()) {
_reset();
} else {
throw Exception('断路器开启状态');
}
}
try {
var result = await operation();
_onSuccess();
return result;
} catch (e) {
_onFailure();
rethrow;
}
}
}
4.3 异常监控和日志
异常监控器
class ExceptionMonitor {
final Map<String, int> _exceptionCounts = {};
final List<ExceptionRecord> _recentExceptions = [];
void recordException(Exception exception, String context, [StackTrace? stackTrace]) {
var exceptionType = exception.runtimeType.toString();
_exceptionCounts[exceptionType] = (_exceptionCounts[exceptionType] ?? 0) + 1;
var record = ExceptionRecord(
exception: exception,
context: context,
timestamp: DateTime.now(),
stackTrace: stackTrace,
);
_recentExceptions.add(record);
_logException(record);
_checkForPatterns(record);
}
}
结构化日志记录
void _logException(ExceptionRecord record) {
var logEntry = {
'level': _getLogLevel(record.exception),
'timestamp': record.timestamp.toIso8601String(),
'exception_type': record.exception.runtimeType.toString(),
'message': record.exception.toString(),
'context': record.context,
'stack_trace': record.stackTrace?.toString(),
};
print('异常日志: ${jsonEncode(logEntry)}');
}
5. 最佳实践
5.1 设计原则
快速失败(Fail Fast)
void processUser(String? name, int? age) {
// 在方法开始时立即验证参数
if (name == null || name.trim().isEmpty) {
throw ArgumentError.notNull('name');
}
if (age == null || age < 0 || age > 150) {
throw ArgumentError.value(age, 'age', '年龄必须在0-150之间');
}
// 后续处理逻辑...
}
异常安全保证
class BankAccount {
void transferTo(BankAccount target, double amount) {
if (amount > _balance) {
throw StateError('余额不足');
}
// 保存原始状态
var originalBalance = _balance;
var originalTargetBalance = target._balance;
try {
// 执行转账
_balance -= amount;
target._balance += amount;
// 可能失败的操作
_notifyTransfer(amount);
} catch (e) {
// 回滚状态
_balance = originalBalance;
target._balance = originalTargetBalance;
rethrow;
}
}
}
资源管理
T using<T>(Disposable resource, T Function(Disposable) action) {
try {
return action(resource);
} finally {
resource.dispose();
}
}
// 使用示例
var result = using(DatabaseConnection(), (conn) {
conn.connect();
return conn.query('SELECT * FROM users');
});
5.2 异常处理指导原则
- 明确异常类型: 使用具体的异常类型而不是通用的
Exception - 提供有用信息: 异常消息应该清楚地描述问题和可能的解决方案
- 避免空异常: 不要捕获异常后什么都不做
- 适当的异常级别: 在正确的抽象层次处理异常
- 文档化异常: 在 API 文档中说明可能抛出的异常
5.3 错误码 vs 异常
使用异常的场景
- 异常情况(预期之外的错误)
- 无法在本地处理的错误
- 需要中断正常执行流程的错误
使用错误码的场景
- 预期内的失败情况
- 性能敏感的代码
- 需要详细错误信息的场景
// 异常方式
int divide(int a, int b) {
if (b == 0) throw ArgumentError('除数不能为零');
return a ~/ b;
}
// 错误码方式
class DivisionResult {
final int? value;
final String? error;
const DivisionResult.success(this.value) : error = null;
const DivisionResult.error(this.error) : value = null;
bool get isSuccess => error == null;
}
DivisionResult safeDivide(int a, int b) {
if (b == 0) return DivisionResult.error('除数不能为零');
return DivisionResult.success(a ~/ b);
}
6. 性能优化
6.1 异常处理性能考虑
避免使用异常进行控制流
// 错误方式
try {
for (int i = 0; ; i++) {
print(list[i]); // 依赖异常结束循环
}
} catch (e) {
// 处理结束
}
// 正确方式
for (int i = 0; i < list.length; i++) {
print(list[i]);
}
异常对象池化
class ExceptionPool {
final Queue<PooledException> _available = Queue<PooledException>();
PooledException getException(String message) {
if (_available.isNotEmpty) {
var exception = _available.removeFirst();
exception.updateMessage(message);
return exception;
}
return PooledException(message);
}
void returnException(PooledException exception) {
_available.add(exception);
}
}
6.2 预分配和缓存
常用异常的预分配
class CommonExceptions {
static final argumentNull = ArgumentError.notNull('argument');
static final invalidState = StateError('invalid state');
static final timeout = TimeoutException('operation timeout');
// 根据需要创建新实例
static ArgumentError argumentValue(dynamic value, String name) {
return ArgumentError.value(value, name);
}
}
7. 测试策略
7.1 异常测试框架
基本测试方法
void expectException<T extends Exception>(
void Function() action,
String testName
) {
try {
action();
print('✗ $testName: 失败 - 期望异常但未抛出');
} on T {
print('✓ $testName: 通过');
} catch (e) {
print('✗ $testName: 失败 - 异常类型错误: ${e.runtimeType}');
}
}
void expectExceptionWithMessage(
void Function() action,
String expectedMessage,
String testName,
) {
try {
action();
print('✗ $testName: 失败 - 期望异常但未抛出');
} catch (e) {
if (e.toString().contains(expectedMessage)) {
print('✓ $testName: 通过');
} else {
print('✗ $testName: 失败 - 异常消息不匹配');
}
}
}
异步异常测试
Future<void> testAsyncException() async {
try {
await Future.delayed(Duration(seconds: 2))
.timeout(Duration(milliseconds: 500));
print('✗ 超时测试失败 - 期望超时异常');
} on TimeoutException {
print('✓ 超时测试通过');
} catch (e) {
print('✗ 超时测试失败 - 异常类型错误: ${e.runtimeType}');
}
}
7.2 Mock 和测试双重
异常注入
class MockRepository implements Repository {
bool shouldThrowException = false;
Exception? exceptionToThrow;
@override
Future<User> findById(String id) async {
if (shouldThrowException && exceptionToThrow != null) {
throw exceptionToThrow!;
}
return User(id: id, name: 'Test User');
}
}
// 测试使用
void testExceptionHandling() {
var mockRepo = MockRepository();
var service = UserService(mockRepo);
// 测试正常情况
mockRepo.shouldThrowException = false;
expectNoException(() async {
await service.getUser('123');
}, '正常获取用户');
// 测试异常情况
mockRepo.shouldThrowException = true;
mockRepo.exceptionToThrow = DataAccessException('连接失败', 'DB_ERROR');
expectException<SystemException>(() async {
await service.getUser('123');
}, '数据库异常处理');
}
8. 调试技巧
8.1 异常调试工具
调试断点
void debugBreakpoint(String message, {bool condition = true}) {
if (condition && kDebugMode) {
print('调试断点: $message');
// 在开发环境中可以使用 debugger(); 语句
}
}
// 使用示例
try {
performComplexOperation();
} catch (e, stackTrace) {
debugBreakpoint('复杂操作失败: $e');
// 详细的调试信息
_printExceptionDetails(e, stackTrace);
}
异常详情打印
void _printExceptionDetails(dynamic exception, StackTrace stackTrace) {
print('异常详情:');
print(' 类型: ${exception.runtimeType}');
print(' 消息: $exception');
print(' 时间: ${DateTime.now().toIso8601String()}');
if (exception is AppException) {
print(' 代码: ${exception.code}');
print(' 原始错误: ${exception.originalError}');
}
print(' 调用栈:');
var stackLines = stackTrace.toString().split('\n');
for (int i = 0; i < min(10, stackLines.length); i++) {
print(' ${stackLines[i]}');
}
}
8.2 性能分析
异常性能统计
class ExceptionProfiler {
final Map<String, ExceptionStats> _stats = {};
void recordException(Exception exception, Duration handlingTime) {
var type = exception.runtimeType.toString();
var stats = _stats.putIfAbsent(type, () => ExceptionStats());
stats.count++;
stats.totalTime += handlingTime;
stats.lastOccurrence = DateTime.now();
}
void printStats() {
print('\n异常处理性能统计:');
_stats.forEach((type, stats) {
print('$type:');
print(' 次数: ${stats.count}');
print(' 总时间: ${stats.totalTime.inMicroseconds}μs');
print(' 平均时间: ${stats.totalTime.inMicroseconds ~/ stats.count}μs');
print(' 最后发生: ${stats.lastOccurrence}');
});
}
}
class ExceptionStats {
int count = 0;
Duration totalTime = Duration.zero;
DateTime? lastOccurrence;
}
8.3 日志集成
结构化异常日志
class ExceptionLogger {
static void logException(
Exception exception,
String context,
StackTrace? stackTrace,
{Map<String, dynamic>? additionalData}
) {
var logEntry = {
'timestamp': DateTime.now().toIso8601String(),
'level': _getLogLevel(exception),
'exception_type': exception.runtimeType.toString(),
'message': exception.toString(),
'context': context,
'stack_trace': stackTrace?.toString(),
...?additionalData,
};
// 发送到日志系统
_sendToLogSystem(logEntry);
}
static String _getLogLevel(Exception exception) {
if (exception is ValidationException) return 'WARNING';
if (exception is BusinessException) return 'INFO';
if (exception is SystemException) return 'ERROR';
return 'CRITICAL';
}
static void _sendToLogSystem(Map<String, dynamic> logEntry) {
// 实际实现中这里会发送到外部日志系统
print('日志: ${jsonEncode(logEntry)}');
}
}
9. 实际应用场景
9.1 Web API 异常处理
class ApiExceptionHandler {
static Map<String, dynamic> handleException(Exception exception) {
if (exception is ValidationException) {
return {
'status': 400,
'error': {
'code': 'VALIDATION_ERROR',
'message': exception.message,
'details': exception.errors,
}
};
}
if (exception is BusinessException) {
return {
'status': 422,
'error': {
'code': exception.code,
'message': exception.message,
}
};
}
if (exception is SecurityException) {
return {
'status': exception.code == 'UNAUTHORIZED' ? 401 : 403,
'error': {
'code': exception.code,
'message': exception.message,
}
};
}
// 系统异常不暴露详细信息
return {
'status': 500,
'error': {
'code': 'INTERNAL_ERROR',
'message': '服务器内部错误',
}
};
}
}
9.2 数据库操作异常处理
class DatabaseService {
Future<User> createUser(Map<String, dynamic> userData) async {
try {
var user = await _database.insert('users', userData);
return User.fromMap(user);
} on PostgreSQLException catch (e) {
if (e.code == '23505') { // 唯一约束违反
throw BusinessException('用户已存在', 'USER_EXISTS');
}
throw DataAccessException('数据库操作失败', 'DB_ERROR', originalError: e);
} on TimeoutException {
throw DataAccessException('数据库操作超时', 'DB_TIMEOUT');
} catch (e) {
throw SystemException('用户创建失败', 'USER_CREATE_FAILED', originalError: e);
}
}
}
9.3 文件操作异常处理
class FileService {
Future<String> readFile(String path) async {
try {
var file = File(path);
if (!await file.exists()) {
throw BusinessException('文件不存在', 'FILE_NOT_FOUND');
}
return await file.readAsString();
} on PathAccessException catch (e) {
throw BusinessException('无权限访问文件', 'ACCESS_DENIED', originalError: e);
} on FileSystemException catch (e) {
if (e.osError?.errorCode == 2) { // 文件不存在
throw BusinessException('文件不存在', 'FILE_NOT_FOUND', originalError: e);
}
throw SystemException('文件系统错误', 'FILE_SYSTEM_ERROR', originalError: e);
} catch (e) {
throw SystemException('文件读取失败', 'FILE_READ_FAILED', originalError: e);
}
}
}
10. 总结
10.1 关键要点
- 异常应该是异常的: 只在真正异常的情况下使用异常
- 快速失败: 尽早发现和报告错误
- 异常安全: 确保异常发生时程序状态的一致性
- 适当的抽象层次: 在正确的层次处理和转换异常
- 完整的错误信息: 提供足够的信息来诊断和解决问题
- 资源管理: 确保异常发生时正确清理资源
- 监控和日志: 建立完善的异常监控和日志系统
10.2 避免的反模式
- 空的 catch 块: 捕获异常后什么都不做
- 过度捕获: 捕获过于宽泛的异常类型
- 异常控制流: 使用异常进行正常的程序控制
- 异常链断裂: 丢失原始异常信息
- 资源泄漏: 异常发生时未正确清理资源
- 性能问题: 在性能敏感的代码中频繁使用异常
10.3 进阶学习建议
- 研究 Dart 官方异常处理最佳实践
- 学习其他语言的异常处理模式
- 实践企业级应用的异常处理架构
- 掌握异常处理的性能优化技巧
- 建立完善的异常监控和告警系统
通过遵循这些原则和最佳实践,您可以构建出健壮、可维护的 Dart 应用程序,能够优雅地处理各种异常情况。