错误处理
灵动桌面框架使用结构化错误系统(src-tauri/src/error.rs),确保错误信息从后端安全传递到前端。
两种错误类型
| 类型 | 用途 | 使用位置 |
|---|---|---|
AppError | 内部业务错误(thiserror 枚举) | Service 层、Database 层 |
CommandError | IPC 返回错误,结构为 { code, message } | Command 层(前端可见) |
AppError 的变体:
#[derive(Debug, Error)]
pub enum AppError {
#[error("IO 错误: {0}")]
Io(#[from] std::io::Error),
#[error("数据库错误: {0}")]
Database(#[from] rusqlite::Error),
#[error("JSON 解析错误: {0}")]
Json(#[from] serde_json::Error),
#[error("未找到: {0}")]
NotFound(String),
#[error("参数无效: {0}")]
InvalidInput(String),
#[error("{0}")]
Custom(String),
}
Io、Database、Json 带 #[from],对应的底层错误可直接用 ? 转成 AppError。
错误转换链
rusqlite / io / serde_json 错误 → AppError → CommandError → 前端 catch
impl From<AppError> for CommandError 按变体映射错误码(见 错误码速查),message 取 AppError 的 Display 文本(含「未找到: 」「参数无效: 」等前缀)。Command 中统一这样转换:
ConfigService::get(&state.db, &key).map_err(|e| e.into())
CommandError 实现了 Serialize,Tauri 会把它序列化成 JSON 对象作为 invoke 的拒绝值,前端 catch 拿到的就是 { code, message } 对象。error.rs 中另有 From<CommandError> for String 与 From<AppError> for String,仅为向后兼容保留。
前端错误处理
import { message } from 'antd'
import { configApi, type CommandError } from '@/lib/api'
try {
const theme = await configApi.get('theme')
} catch (e) {
const err = e as CommandError
// err: { code: "NOT_FOUND", message: "未找到: 配置项 'theme' 不存在" }
message.error(err.message)
}
WARNING
src/lib/api/client.ts 中的 parseCommandError / getErrorMessage / getErrorCode 目前按“错误是 JSON 字符串”解析,传入对象时会退化为 { code: "UNKNOWN", message: "[object Object]" };同理 String(e) 也只会得到 [object Object]。读取后端错误时请直接访问 code / message 字段。
规范
- 禁止使用
unwrap()— 使用?运算符 - 禁止使用
panic!()— 返回Err(AppError::...) Mutex加锁失败用map_err(|e| AppError::Custom(e.to_string()))转换,不要lock().unwrap()- 所有错误必须处理或向上传播