错误
Actix使用自己的actix_web::error::Error类型和 actix_web::error::ResponseError特征来处理Web处理程序的错误。
如果处理程序返回一个Error
(指一般的Rust特征 std::error::Error
)Result也实现了 ResponseError
特征,actix会将该错误呈现为HTTP响应。 ResponseError
有一个名为return的函数error_response()
返回HttpResponse
:
pub trait ResponseError: Fail {
fn error_response(&self) -> HttpResponse {
HttpResponse::new(StatusCode::INTERNAL_SERVER_ERROR)
}
}
一个Responder
强制将兼容Result
转换的HTTP响应:
impl<T: Responder, E: Into<Error>> Responder for Result<T, E>
Error
在上面的代码中是actix的错误定义,并且实现的任何错误ResponseError
都可以自动转换为一个。
Actix-web提供ResponseError
一些常见的非actix错误的实现。例如,如果处理程序以a响应io::Error
,则该错误将转换为HttpInternalServerError
:
use std::io;
fn index(req: &HttpRequest) -> io::Result<fs::NamedFile> {
Ok(fs::NamedFile::open("static/index.html")?)
}
有关外部实现的完整列表,请参阅actix-web API文档ResponseError
。
自定义错误响应的示例
以下是一个示例实现 ResponseError
:
use actix_web::*;
#[derive(Fail, Debug)]
#[fail(display="my error")]
struct MyError {
name: &'static str
}
// Use default implementation for `error_response()` method
impl error::ResponseError for MyError {}
fn index(req: &HttpRequest) -> Result<&'static str, MyError> {
Err(MyError{name: "test"})
}
ResponseError
有一个默认的实现error_response()
,它将呈现500(内部服务器错误),这就是index
上面执行处理程序时会发生的事情 。
覆盖error_response()
以产生更有用的结果:
#[macro_use] extern crate failure;
use actix_web::{App, HttpRequest, HttpResponse, http, error};
#[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 {
match *self {
MyError::InternalError => HttpResponse::new(
http::StatusCode::INTERNAL_SERVER_ERROR),
MyError::BadClientData => HttpResponse::new(
http::StatusCode::BAD_REQUEST),
MyError::Timeout => HttpResponse::new(
http::StatusCode::GATEWAY_TIMEOUT),
}
}
}
fn index(req: &HttpRequest) -> Result<&'static str, MyError> {
Err(MyError::BadClientData)
}
Error helpers
Actix提供了一组错误辅助函数,可用于从其他错误生成特定的HTTP错误代码。在这里,我们使用以下方法将MyError
未实现ResponseError
特征的转换为400(错误请求) map_err
:
# extern crate actix_web;
use actix_web::*;
#[derive(Debug)]
struct MyError {
name: &'static str
}
fn index(req: &HttpRequest) -> Result<&'static str> {
let result: Result<&'static str, MyError> = Err(MyError{name: "test"});
Ok(result.map_err(|e| error::ErrorBadRequest(e.name))?)
}
有关可用错误帮助程序的完整列表,请参阅actix-web error模块的API文档。
与failure的兼容性
Actix-web提供与failure库的自动兼容性,以便将错误派生fail
自动转换为actix错误。请记住,这些错误将使用默认的500状态代码呈现,除非您还error_response()
为它们提供自己的实现。
Error logging
Actix在WARN
日志级别记录所有错误。如果应用程序的日志级别设置为DEBUG并RUST_BACKTRACE
启用,则还会记录回溯。这些可以配置环境变量:
>> RUST_BACKTRACE=1 RUST_LOG=actix_web=debug cargo run
该Error
类型使用cause的错误回溯(如果可用)。如果基础故障不提供回溯,则构造新的回溯指向转换发生的点(而不是错误的起源)。
错误处理的推荐做法
考虑将应用程序产生的错误划分为两大类可能是有用的:那些旨在面向用户的错误和那些不是面向用户的错误。
前者的一个例子是我可能会使用failure来指定一个UserError
枚举ValidationError
以便在用户发送错误输入时返回:
#[macro_use] extern crate failure;
use actix_web::{HttpResponse, http, error};
#[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 {
match *self {
UserError::ValidationError { .. } => HttpResponse::new(
http::StatusCode::BAD_REQUEST),
}
}
}
这将完全按预期运行,因为定义的错误消息 display
是用明确的意图写入的,用户可以读取。
但是,发送错误消息对于所有错误都是不可取的 - 在服务器环境中发生许多故障,我们可能希望从用户隐藏这些特定信息。例如,如果数据库出现故障并且客户端库开始产生连接超时错误,或者HTML模板格式不正确以及呈现时出错。在这些情况下,可能最好将错误映射到适合用户使用的一般错误。
这是一个InternalError
使用自定义消息将内部错误映射到面向用户的示例:
#[macro_use] extern crate failure;
use actix_web::{App, HttpRequest, HttpResponse, http, error, fs};
#[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 {
match *self {
UserError::InternalError => HttpResponse::new(
http::StatusCode::INTERNAL_SERVER_ERROR),
}
}
}
fn index(_: &HttpRequest) -> Result<&'static str, UserError> {
fs::NamedFile::open("static/index.html").map_err(|_e| UserError::InternalError)?;
Ok("success!")
}
通过将错误划分为面向用户和非面向错误的错误,我们可以确保我们不会意外地将用户暴露给他们不应该看到的应用程序内部错误。