# spring-axum **Repository Path**: luzhihaoTestingLab/spring-axum ## Basic Information - **Project Name**: spring-axum - **Description**: 基于rust axum写的一个类springboot web框架 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-10-13 - **Last Updated**: 2025-10-14 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Spring Axum This framework provides: - Macro routing `#[route_get/route_post/route_put]` with optional `layer = ` for route-level middleware. - Unified error handling via `spring_axum::AppError` and `AppResult`. - JSON serialization with `Json` and typed headers using `axum-extra`. - A sample client using `reqwest`. - Transaction & cache macros for service methods. - Component/Controller/Interceptor discovery via inventory. ## 快速开始 将框架依赖加入你的项目: ``` [dependencies] spring-axum = { path = "./spring-axum" } spring-axum-macros = { path = "./spring-axum-macros" } ``` 在你的 `main.rs` 中: ```rust #[tokio::main] async fn main() -> anyhow::Result<()> { let app = spring_axum::auto_discover!(); app.run().await } ``` ## Patterns - Route-level middleware: ```rust #[route_get("/path", layer = spring_axum::InterceptorLayer::new(LogInterceptor))] async fn handler() -> String { "ok".into() } ``` - Unified errors: ```rust use spring_axum::{AppError, AppResult, Json, JsonRejection}; #[route_post("/items")] async fn create(payload: Result, JsonRejection>) -> AppResult<()> { let Json(item) = payload.map_err(AppError::from)?; // ... Ok(()) } ``` - Typed headers: ```rust use axum_extra::{TypedHeader, typed_header::ContentType}; #[route_get("/headers")] async fn headers() -> (TypedHeader, Json) { (TypedHeader(ContentType::json()), Json(json!({"ok":true}))) } ``` ## 宏用法示例(框架宏) 以下示例来自 `spring-axum-macros`,可在 demo 或你的项目中直接使用。 ### 路由宏:`#[route_get] / #[route_post] / #[route_put] / #[route_delete]` ```rust use spring_axum::{Json}; use serde_json::Value; #[route_get("/ping")] async fn ping() -> String { "pong".into() } #[route_post("/items", layer = spring_axum::InterceptorLayer::new(LogInterceptor))] async fn create_item(payload: Json) -> String { format!("created: {}", payload.0) } ``` 这些宏会为每个函数生成一个 `__spring_axum_route_` 的路由构建函数,可被控制器宏自动合并。 ### 校验宏:`#[validate_json] / #[validate_query] / #[validate_form] / #[validate_json_stream]` 在启用 `spring-axum` 的 `validator` 和(如需要)`axum_extra_json` 特性后,将 `Json/Query/Form` 自动替换为带 `validator` 的 `Validated*` 提取器。 ```rust use serde::Deserialize; use validator::Validate; #[derive(Deserialize, Validate)] struct CreateReq { #[validate(length(min = 1))] name: String } #[validate_json] #[route_post("/validate")] async fn validate_endpoint(payload: Json) -> String { // 如果校验失败,框架会返回 400,并携带错误详情 format!("ok: {}", payload.0.name) } ``` 启用方式:在 `spring-axum` 的 features 中加入 `validator`,如 `spring-axum = { features = ["validator"] }`。 ### 控制器宏:`#[controller(routes...)]` 将多个路由合并为一个可发现的控制器,框架会自动从 `inventory` 注册表中收集。 ```rust use spring_axum::controller; #[controller(ping, create_item)] pub struct Api; // 现在 `SpringApp::with_discovered_controllers()` 会自动把这些路由合并到应用中 ``` ### 组件宏:`#[component]` 用于注册一个 `Default` 组件到全局 `ApplicationContext`,可在处理函数中通过 `Component` 提取器注入。 ```rust use spring_axum::{component, Component}; #[component] #[derive(Default, Clone)] pub struct Greeter { pub prefix: String } #[route_get("/greet/{name}")] async fn greet(Component(g): Component, spring_axum::Path(name): spring_axum::Path) -> String { format!("{}{}", g.prefix, name) } ``` ### 拦截器宏:`#[interceptor]` 用于声明一个实现了默认构造的拦截器,并自动把其应用注册到 `inventory`,可通过 `SpringApp::with_discovered_interceptors()`启用。 ```rust use spring_axum::{interceptor, Interceptor, InterceptorLayer}; #[interceptor] #[derive(Default, Clone)] pub struct LogInterceptor; impl Interceptor for LogInterceptor { fn on_request(&self, req: spring_axum::AxumRequest) -> spring_axum::AxumRequest { req } fn on_response(&self, res: spring_axum::AxumResponse) -> spring_axum::AxumResponse { res } } ``` ### 事务宏(方法级):`#[transactional]` 将单个 `async fn` 的方法体包裹进框架的事务函数 `spring_axum::transaction(...)`。 ```rust use spring_axum::transactional; pub struct OrderService; impl OrderService { #[transactional] pub async fn place_order(&self, order_id: i64) -> spring_axum::AppResult<()> { // 在事务中执行 Ok(()) } } ``` ### 服务宏(impl 级):`#[tx_service]` 与 `#[non_tx]` 为某个 `impl` 中的所有方法默认应用事务包裹,若某个方法不需要事务则标注 `#[non_tx]`。 ```rust use spring_axum_macros::{tx_service, non_tx}; #[tx_service] impl UserService { pub async fn save_profile(&self, uid: i64) -> spring_axum::AppResult<()> { Ok(()) } #[non_tx] pub async fn health_check(&self) -> spring_axum::AppResult<()> { Ok(()) } } ``` ### 缓存宏:`#[cacheable] / #[cache_put] / #[cache_evict]` 为方法添加内存缓存(可选 TTL)、主动更新或删除缓存键。返回类型需为 `AppResult`。 ```rust use spring_axum_macros::{cacheable, cache_put, cache_evict}; #[cacheable(ttl_secs = 30)] #[route_get("/profile/{uid}")] async fn get_profile(spring_axum::Path(uid): spring_axum::Path) -> spring_axum::AppResult { Ok(format!("user:{}", uid)) } #[cache_put(ttl_secs = 10)] async fn update_profile(uid: i64, name: String) -> spring_axum::AppResult { Ok(name) } #[cache_evict] async fn evict_profile(uid: i64) -> spring_axum::AppResult<()> { Ok(()) } ``` ### 事件监听宏:`#[event_listener(T)]` 将函数注册为某事件类型 `T` 的监听器,事件分发机制可按需接入。 ```rust use spring_axum_macros::event_listener; struct UserCreated { id: i64 } #[event_listener(UserCreated)] fn on_user_created(evt: UserCreated) { // 处理事件 } ``` ### SQL/Mapper 宏:`#[sql]` / `#[mapper(namespace = "...")]` 在使用 MyBatis 集成时,可通过宏快速生成方法或 Mapper。注意:`#[mapper]` 需标注在 `impl` 块上。 ```rust use spring_axum_macros::{sql, mapper}; pub struct UserMapper; #[mapper(namespace = "user")] impl UserMapper { #[sql] pub fn insert(&self, id: i64, name: String) -> spring_axum::AppResult<()> { Ok(()) } } ``` ## 集成与特性 - 发现机制:调用 `SpringApp::with_discovered_components().with_discovered_interceptors().with_discovered_controllers().with_discovered_advices()` 自动装配。 - 校验:启用 `spring-axum` feature `validator`;JSON 流校验需要 `axum_extra_json`。 - Swagger:启用 `spring-axum` feature `swagger`。 - SQLx(Postgres):启用 `spring-axum` feature `sqlx_postgres`,并设置环境变量 `DATABASE_URL`,框架会懒加载连接池。 示例: ```rust #[tokio::main] async fn main() -> anyhow::Result<()> { // 自动初始化:MyBatis、SQLx(如果启用)、缓存等 let app = spring_axum::auto_discover!() .with_router(spring_axum::Router::new()) .with_discovered_components() .with_discovered_interceptors() .with_discovered_controllers() .with_discovered_advices(); app.run().await } ```