Actix-web使用自己的actix_web::error::Error
类型和actix_web::error::ResponseError
trait来从Web处理程序处理错误。
如果handler在还实现了ResponseError
trait的Result
中返回Error
(指的是一般的Rust trait std::error::Error
),则actix-web会将错误与相应的actix_web::http::StatusCode
一起呈现为HTTP响应。
默认情况下会生成内部服务器错误:
pub trait ResponseError {
fn error_response(&self) -> HttpResponse;
fn status_code(&self) -> StatusCode;
}
Responder
将兼容Result
强制转换为HTTP响应:
impl<T: Responder, E: Into<Error>> Responder for Result<T, E>
上面代码中的Error
是actix-web的error定义,任何实现ResponseError
的errors都可以自动转换为一个。
Actix-web提供了一些常见的非actix错误的ResponseError
实现。例如,如果handler以io::Error
响应,则该错误将转换为HttpInternalServerError
:
use std::io;
fn index(_req: HttpRequest) -> io::Result<fs::NamedFile> {
Ok(fs::NamedFile::open("static/index.html")?)
}
有关ResponseError
的外部实现的完整列表,请参见actix-web API文档。
这是ResponseError
的示例实现:
use actix_web::{error, Result};
use failure::Fail;
#[derive(Fail, Debug)]
#[fail(display = "my error")]
struct MyError {
name: &'static str,
}
// Use default implementation for `error_response()` method
impl error::ResponseError for MyError {}
async fn index() -> Result<&'static str, MyError> {
Err(MyError { name: "test" })
}
ResponseError
具有error_response()
的默认实现,它将呈现500 (服务器内部错误),并且在上面执行index
处理程序时会发生这种情况。
覆盖error_response()
以产生更多有用的结果:
use actix_http::ResponseBuilder;
use actix_web::{error, http::header, http::StatusCode, HttpResponse};
use failure::Fail;
#[derive(Fail, Debug)]
enum MyError {
#[fail(display = "internal error")]
InternalError,
#[fail(display = "bad request")]
BadClientData,
#[fail(display = "timeout")]
Timeout,
}
impl error::ResponseError for MyError {
fn error_response(&self) -> HttpResponse {
ResponseBuilder::new(self.status_code())
.set_header(header::CONTENT_TYPE, "text/html; charset=utf-8")
.body(self.to_string())
}
fn status_code(&self) -> StatusCode {
match *self {
MyError::InternalError => StatusCode::INTERNAL_SERVER_ERROR,
MyError::BadClientData => StatusCode::BAD_REQUEST,
MyError::Timeout => StatusCode::GATEWAY_TIMEOUT,
}
}
}
async fn index() -> Result<&'static str, MyError> {
Err(MyError::BadClientData)
}
Actix-web提供了一组error helper函数,这些函数可用于从其他错误中生成特定的HTTP错误代码。
在这里,我们使用map_err
将未实现ResponseError
trait的MyError
转换为_400_(错误请求):
use actix_web::{error, Result};
#[derive(Debug)]
struct MyError {
name: &'static str,
}
async fn index() -> Result<&'static str> {
let result: Result<&'static str, MyError> = Err(MyError { name: "test error" });
Ok(result.map_err(|e| error::ErrorBadRequest(e.name))?)
}
有关可用的错误帮助程序的完整列表,请查看actix-web错误模块的API文档。
Actix-web提供与failure库的自动兼容性,以便将导致故障的错误自动转换为actix错误。
请记住,这些错误将使用默认的_500_状态码呈现,除非您还为其提供了自己的error_response()
实现。
Actix在WARN
日志级别记录所有错误。如果应用程序的日志级别设置为DEBUG
,并且启用了RUST_BACKTRACE
,则还将记录backtrace(回溯)。这些可以使用环境变量进行配置:
>> RUST_BACKTRACE=1 RUST_LOG=actix_web=debug cargo run
Error
类型使用cause的错误回溯(如果有)。如果根本的故障不提供回溯,则构造一个新的回溯,指向发生转换的点(而不是错误的根源)。
考虑将应用程序产生的错误分为两大类可能是有用的:一类是面向用户的,二类不是面向用户的。
前者的一个示例是,我可能使用failure(失败)指定UserError
枚举,该枚举封装了一个ValidationError
,以便在用户发送错误的输入时返回:
use actix_http::ResponseBuilder;
use actix_web::{error, http::header, http::StatusCode, HttpResponse};
use failure::Fail;
#[derive(Fail, Debug)]
enum UserError {
#[fail(display = "Validation error on field: {}", field)]
ValidationError { field: String },
}
impl error::ResponseError for UserError {
fn error_response(&self) -> HttpResponse {
ResponseBuilder::new(self.status_code())
.set_header(header::CONTENT_TYPE, "text/html; charset=utf-8")
.body(self.to_string())
}
fn status_code(&self) -> StatusCode {
match *self {
UserError::ValidationError { .. } => StatusCode::BAD_REQUEST,
}
}
}
这将完全按照预期的方式运行,因为用display
定义的错误消息是在明确意图下被用户读取的。
但是,并非所有错误都希望发送回错误消息-在服务器环境中会发生很多故障,我们可能希望向用户隐藏具体信息。 例如,如果数据库关闭并且客户端库开始产生连接超时错误,或者HTML模板格式不正确,并且渲染时发生错误。 在这些情况下,最好将错误映射为适合用户使用的一般错误。
这是一个使用自定义消息将内部错误映射到面向用户的InternalError
的示例:
use actix_http::ResponseBuilder;
use actix_web::{error, http::header, http::StatusCode, HttpResponse};
use failure::Fail;
#[derive(Fail, Debug)]
enum UserError {
#[fail(display = "An internal error occurred. Please try again later.")]
InternalError,
}
impl error::ResponseError for UserError {
fn error_response(&self) -> HttpResponse {
ResponseBuilder::new(self.status_code())
.set_header(header::CONTENT_TYPE, "text/html; charset=utf-8")
.body(self.to_string())
}
fn status_code(&self) -> StatusCode {
match *self {
UserError::InternalError => StatusCode::INTERNAL_SERVER_ERROR,
}
}
}
async fn index() -> Result<&'static str, UserError> {
do_thing_that_failes().map_err(|_e| UserError::InternalError)?;
Ok("success!")
}
通过将错误分为面对用户的错误和不面对用户的错误,我们可以确保我们不会意外地使用户暴露于应用程序内部抛出的错误中,而这些错误并非本应引起的。
这是使用middleware::Logger
的基本示例:
use actix_web::{error, Result};
use failure::Fail;
use log::debug;
#[derive(Fail, Debug)]
#[fail(display = "my error")]
pub struct MyError {
name: &'static str,
}
// Use default implementation for `error_response()` method
impl error::ResponseError for MyError {}
async fn index() -> Result<&'static str, MyError> {
let err = MyError { name: "test error" };
debug!("{}", err);
Err(err)
}
#[actix_rt::main]
async fn main() -> std::io::Result<()> {
use actix_web::{middleware::Logger, web, App, HttpServer};
std::env::set_var("RUST_LOG", "my_errors=debug,actix_web=info");
std::env::set_var("RUST_BACKTRACE", "1");
env_logger::init();
HttpServer::new(|| {
App::new()
.wrap(Logger::default())
.route("/", web::get().to(index))
})
.bind("127.0.0.1:8088")?
.run()
.await
}