高效实现单个实体查询的宏解决方案
精准查询:使用Rust宏优化单个实体检索
在Web应用中,根据ID查询单个实体是最常见的操作之一。本文将介绍如何使用过程宏来自动化这一过程,支持多种ID类型并提供统一的错误处理。
传统方式的挑战
传统的手动实现方式:
rustpub async fn get_category( db: &DatabaseConnection, id: i32, ) -> Result<category::Model, AppError> { category::Entity::find_by_id(id) .one(db) .await .map_err(|e| AppError::DatabaseError(e.to_string()))? .ok_or_else(|| AppError::NotFound("Category not found".to_string())) }
每个实体都需要重复编写相似的代码。
宏驱动的解决方案
rustcrud_entity!({ entity: categories, route_prefix: "/api/categories", permission_prefix: "categories", id_type: "integer", operations: ["read"] });
核心实现
1. 多ID类型支持
rustlet (id_rust_type, find_method, path_param_type) = match id_type { IdType::Uuid => ( quote! { String }, quote! { find_by_uuid }, quote! { String }, ), IdType::Integer => (quote! { i32 }, quote! { find_by_id }, quote! { i32 }), IdType::Custom(custom_type) => { let custom_ident = format_ident!("{}", custom_type); ( quote! { #custom_ident }, quote! { find_by_id }, quote! { #custom_ident }, ) } };
2. 查询逻辑生成
rustfn generate_read_code( entity: &Ident, route_prefix: &LitStr, permission_prefix: &LitStr, id_rust_type: &proc_macro2::TokenStream, find_method: &proc_macro2::TokenStream, path_param_type: &proc_macro2::TokenStream, ) -> proc_macro2::TokenStream { let get_fn = format_ident!("get_{}", entity.to_string().to_lowercase()); let get_handler = format_ident!("get_{}_handler", entity.to_string().to_lowercase()); quote! { /// 获取实体 pub async fn #get_fn( db: &DatabaseConnection, id: #id_rust_type, ) -> Result<#entity::Model, AppError> { #entity::Entity::#find_method(&id) .one(db) .await .map_err(|e| AppError::DatabaseError(e.to_string()))? .ok_or_else(|| AppError::NotFound(format!("{} not found", id))) } } }
使用示例
1. 整数ID查询
rustcrud_entity!({ entity: categories, route_prefix: "/api/categories", permission_prefix: "categories", id_type: "integer", operations: ["read"] });
生成的路由:GET /api/categories/{id}
2. UUID查询
rustcrud_entity!({ entity: users, route_prefix: "/api/users", permission_prefix: "users", id_type: "uuid", operations: ["read"] });
生成的路由:GET /api/users/{id}
错误处理策略
宏生成的代码包含完善的错误处理:
rustmatch get_categories(db.get_ref(), id).await { Ok(result) => Ok(HttpResponse::Ok().json(result)), Err(e) => Err(e.into()), }
- 找到实体:返回200状态码和实体数据
- 未找到:返回404错误
- 数据库错误:返回500错误
权限集成
自动集成权限验证:
rust#[crate::route_permission( path = #full_path, method = "get", permission = #full_permission )]
性能优化
- 编译时优化:所有代码在编译时生成,无运行时开销
- 零成本抽象:生成的代码与手动编写的一样高效
- 类型安全:编译时检查所有路径参数类型
实际应用场景
1. RESTful API
httpGET /api/categories/1 GET /api/users/550e8400-e29b-41d4-a716-446655440000
2. 前端集成
javascript// 前端调用 const response = await fetch('/api/categories/1'); const category = await response.json();
总结
通过过程宏自动化单个实体查询,我们实现了:
- 支持多种ID类型(整数、UUID、自定义)
- 统一的错误处理
- 自动权限验证
- 类型安全的路径参数
- 一致的API设计
这种方法显著减少了重复代码,提高了开发效率,同时保持了代码的质量和一致性。
