Search code examples
rusterror-handlingactix-web

How to handle a panic in Actix Web when querying database?


Here is the code, I am query a MySQL database in a API:

#[get("/api/v2/tags")]
pub async fn get_tags(
    db: Data<R2D2Pool<MySqlConnectionManager>>
) -> Result<Json<Vec<Tag>>, GenericError> {
    let mut conn: PooledConnection<MySqlConnectionManager> = db.get().unwrap();

    let sql_str = "SELECT * FROM table_non_exist".to_string();
    let res = conn.query_map( sql_str, |(id, tagname, description)| Tag {
        id,
        tagname,
        description
    }).expect("Query failed.");

    println!("{:?}", res);

    Ok(Json(res))
}

If something wrong happened in the SQL string, such as querying a table that is not existed, the program will panic and crash:

thread 'actix-rt|system:0|arbiter:0' panicked at 'Query failed.: MySqlError { ERROR 1146 (42S02): Table 'myblog.some' doesn't exist }', src/api/tag.rs:30:8
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

I'm wondering what is the best way to handle this panic and return an error in JSON format instead of crashing the app?


Solution

  • The error type of the returned Result can be anything implementing ResponseError, so you'd just use Rust's standard Result-based error-handling facilities for this. For example, instead of this, which will panic on error:

    let mut conn: PooledConnection<MySqlConnectionManager> = db.get().unwrap();
    

    You could do this (after suitably adjusting the return type from the handler):

    let mut conn: PooledConnection<MySqlConnectionManager> = db.get()
        .map_err(actix_web::error::ErrorInternalServerError)?;
    

    You'd do the same thing instead of using .expect() to unwrap the Result.

    If you need to be able to handle different kinds of errors, you need to unify them to a single return type. The simplest approach may be to return Result<_, Box<dyn actix_web::error::ResponseError>> which has the advantage of conveying any kind of Actix-compatible error, with the downside that the error needs to be put on the heap. (You can always try Result<_, impl actix_web::error::ResponseError> first to more efficiently handle cases where only one type of error needs to be handled.)

    Consider reading the Actix error handling documentation and the actix_web::error namespace documentation for other functions and types related to error handling.