错误处理
Rust 应用中的常规错误处理方式
Rust 的错误处理不同于 Java 等语言, 它没有 try...catch
这种玩意, 正常的做法是在应用程序层面定义全局的错误处理类型:
use thiserror::Error;
#[derive(Error, Debug)]
pub enum AppError {
#[error("io: `{0}`")]
Io(#[from] io::Error),
#[error("utf8: `{0}`")]
FromUtf8(#[from] FromUtf8Error),
#[error("diesel: `{0}`")]
Diesel(#[from] diesel::result::Error),
...
}
pub type AppResult<T> = Result<T, AppError>;
这里使用了 thiserror
这个库, 它可以方便地定义你自己的自定义错误类型, 简化代码. 为了简单书写, 顺便定义一个 AppResult
.
Handler 中的错误处理
在 Salvo 中, Handler
也经常会遇到各式错误, 比如: 数据库连接错误, 文件访问错误, 网络连接错误等等. 对于这个类型的错误, 可以采用上述的错误处理手法:
#[handler]
async fn home()-> AppResult<()> {
}
这里的 home
就直接返回了一个 AppResult<()>
. 但是, 这个错误该如何显示呢? 我们需要为 AppResult
这个自定义错误类型实现 Writer
, 在这个实现中我们可以决定如何显示错误:
#[async_trait]
impl Writer for AppError {
async fn write(mut self, _req: &mut Request, depot: &mut Depot, res: &mut Response) {
res.render(Text::Plain("I'm a error, hahaha!"));
}
}
Error
中往往包含一些敏感信息, 一般情况下, 并不想被普通用户看到, 那样也太不安全了, 一点点隐私也没有了. 但是, 如果你是开发人员或者网站管理员, 或许想法就不一样了, 你希望错误能把外衣脱得光光的, 让你看到最真实的错误信息.
可以看到, write
的方法中, 我们其实是可以拿到 Request
和 Depot
的引用的, 这就可以很方便地实现上面的骚操作了:
#[async_trait]
impl Writer for AppError {
async fn write(mut self, _req: &mut Request, depot: &mut Depot, res: &mut Response) {
let user = depot.obtain::<User>();
if user.is_admin {
res.render(Text::Plain(e.to_string()));
} else {
res.render(Text::Plain("I'm a error, hahaha!"));
}
}
}
错误页面的显示
Salvo 中自带的错误页面在绝大部分情况下是满足需求的, 它可以根据请求的数据类型显示 Html, Json 或者 Xml 页面. 然而, 某些情况下, 我们依然期望自定义错误页面的显示.
这个可以通过自定义 Catcher
实现. 详细的介绍可以查看 Catcher
部分的讲解.