错误处理
Rust 应用中的常规错误处理方式
Rust 的错误处理不同于 Java 等语言, 它没有 try...catch 这种玩意, 正常的做法是在应用程序层面定义全局的错误处理类型:
这里使用了 thiserror 这个库, 它可以方便地定义你自己的自定义错误类型, 简化代码. 为了简单书写, 顺便定义一个 AppResult.
thiserror 与 anyhow
在 Rust 错误处理的生态中,两个常用的库是 thiserror 和 anyhow:
-
thiserror: 适用于库开发者,用于定义清晰的错误类型。它通过派生宏帮助您为自定义错误类型实现
std::error::Errortrait,同时允许您定义错误的表示方式。当您构建一个库或者需要为用户提供清晰的错误类型时,thiserror是更好的选择。 -
anyhow: 适用于应用开发者,提供了一个通用的错误类型
anyhow::Error,能够包含任何实现了std::error::Errortrait 的错误。它更侧重于错误的传播而非定义,尤其适合应用层代码,您可以快速将各种错误转换为anyhow::Error,减少编写样板代码的需要。
在某些场景中,您可能会同时使用这两个库:在库中使用 thiserror 定义错误类型,在应用中使用 anyhow 处理和传播这些错误。
Handler 中的错误处理
在 Salvo 中, Handler 也经常会遇到各式错误, 比如: 数据库连接错误, 文件访问错误, 网络连接错误等等. 对于这个类型的错误, 可以采用上述的错误处理手法:
这里的 home 就直接返回了一个 AppResult<()>. 但是, 这个错误该如何显示呢? 我们需要为 AppResult 这个自定义错误类型实现 Writer, 在这个实现中我们可以决定如何显示错误:
Salvo 中的 Handler 可以返回 Result,只需要 Result 中的 Ok 和 Err 的类型都实现 Writer trait。
使用 anyhow 进行错误处理
考虑到 anyhow 的使用比较广泛,Salvo 提供了对 anyhow::Error 的内置支持。在开启 anyhow 功能后,anyhow::Error 会实现 Writer trait,它会被映射为 InternalServerError:
要使用 anyhow 功能,需要在 Cargo.toml 中启用 Salvo 的 anyhow feature:
这样,您的处理函数就可以直接返回 anyhow::Result<T>:
Error 中往往包含一些敏感信息, 一般情况下, 并不想被普通用户看到, 那样也太不安全了, 一点点隐私也没有了. 但是, 如果你是开发人员或者网站管理员, 或许想法就不一样了, 你希望错误能把外衣脱得光光的, 让你看到最真实的错误信息.
可以看到, write 的方法中, 我们其实是可以拿到 Request 和 Depot 的引用的, 这就可以很方便地实现上面的骚操作了:
错误页面的显示
Salvo 中自带的错误页面在绝大部分情况下是满足需求的, 它可以根据请求的数据类型显示 Html, Json 或者 Xml 页面. 然而, 某些情况下, 我们依然期望自定义错误页面的显示.
这个可以通过自定义 Catcher 实现. 详细的介绍可以查看 Catcher 部分的讲解.