使用actix-web+Handlebars注入数据库数据时遇类型匹配与序列化错误
问题分析与解决方案
核心问题根源
两段代码的错误本质都是错误处理逻辑不当,导致最终传递给模板的数据类型不符合要求:
index函数中,books_data被错误包装成了嵌套的Result类型,无法直接作为Vec<Book>传入模板结构体book函数中,错误处理将HttpResponse存入了book_data的错误变体,而Handlebars无法序列化HttpResponse类型;同时函数返回类型定义错误,无法兼容数据库和模板渲染错误
修复后的代码示例
1. 修复index函数(获取全部数据)
async fn index( hb: web::Data<Handlebars<'_>>, pool: web::Data<DbPool>, ) -> Result<HttpResponse, Error> { let mut connection = pool.get().expect("Can't get db connection pool"); // 分步处理错误:先处理阻塞任务错误,再处理数据库查询错误 let books_data = web::block(move || books.limit(100).load::<Book>(&mut connection)) .await? // 转换BlockingError为actix-web的Error .map_err(|e| { eprintln!("Database query failed: {}", e); HttpResponse::InternalServerError().finish() })?; // 转换数据库错误为HttpResponse,再转为actix-web的Error // 此时books_data已是纯净的Vec<Book>,类型匹配 let data = IndexTemplateData("Book Store".to_string(), books_data); // 处理模板渲染错误 let body = hb.render("index", &data) .map_err(|e| { eprintln!("Template render failed: {}", e); HttpResponse::InternalServerError().finish() })?; Ok(HttpResponse::Ok().body(body)) }
2. 修复book函数(根据ID获取单条数据)
async fn book( hb: web::Data<Handlebars<'_>>, pool: web::Data<DbPool>, book_id: web::Path<i32>, ) -> Result<HttpResponse, Error> { let book_id = book_id.into_inner(); let mut connection = pool.get().expect("Can't get db connection from pool"); // 处理阻塞任务和数据库查询错误,针对NotFound返回404 let book_data = web::block(move || { books .filter(id.eq(book_id)) .first::<Book>(&mut connection) }) .await? .map_err(|e| { eprintln!("Database query failed: {}", e); match e { diesel::result::Error::NotFound => HttpResponse::NotFound().body("Book not found"), _ => HttpResponse::InternalServerError().finish() } })?; // 此时book_data是纯净的Book实例,可正常序列化 let body = hb.render("book", &book_data) .map_err(|e| { eprintln!("Template render failed: {}", e); HttpResponse::InternalServerError().finish() })?; Ok(HttpResponse::Ok().body(body)) }
额外优化建议
将元组结构体改为具名结构体,模板中数据访问更直观,也更易维护:
#[derive(Serialize, Debug, Clone)] struct IndexTemplateData { title: String, books: Vec<self::model::Book>, } // 使用方式 let data = IndexTemplateData { title: "Book Store".to_string(), books: books_data, };
模板中可直接用{{title}}和{{#each books}}遍历数据,无需依赖{{.0}}、{{.1}}这类不直观的元组索引。
内容的提问来源于stack exchange,提问作者Olayemi Michael
相关产品推荐
相关产品推荐

