exum 0.1.2

A lightweight Axum syntax sugar library
Documentation

Exum

一个轻量级的 Axum 语法糖库,提供更简洁的路由定义语法。

特性

  • 🚀 简洁的路由宏语法
  • 📦 自动参数提取和类型转换
  • 🔧 支持多种HTTP方法
  • 🎯 路径参数自动解析
  • 📝 查询参数和请求体处理
  • ⚡ 省略返回值时默认返回 impl IntoResponse
  • 🌍 环境自动检测和配置覆盖
  • 🔧 环境变量注入支持
  • 📁 多环境配置文件管理

安装

Cargo.toml 中添加依赖:

[dependencies]

exum = "0.1.0"

快速开始

use exum::*;

#[get("/hello/:id")]
async fn hello(id: String, #[q] q: String) -> String {
    format!("id: {}, query: {}", id, q)
}

#[post("/users")]
async fn create_user(#[b] user: User) -> String {
    format!("Created user: {:?}", user)
}

// 省略返回值时,默认返回 impl IntoResponse
#[get("/simple")]
async fn simple_handler() {
    "Hello, World!"
}

#[tokio::main]
async fn main() {
    let app = Application::build(ApplicationConfig::default());
    app.run().await;
}

路由宏

支持的HTTP方法

  • #[get(path)] - GET 请求
  • #[post(path)] - POST 请求
  • #[put(path)] - PUT 请求
  • #[delete(path)] - DELETE 请求
  • #[options(path)] - OPTIONS 请求
  • #[head(path)] - HEAD 请求
  • #[trace(path)] - TRACE 请求
  • #[route(path, method = "METHOD")] - 自定义方法

路径参数

路径参数使用 : 前缀或 {} 语法:

#[get("/users/:id")]
async fn get_user(id: String) -> String {
    format!("User ID: {}", id)
}

#[get("/posts/{post_id}/comments/{comment_id}")]
async fn get_comment(post_id: String, comment_id: String) -> String {
    format!("Post: {}, Comment: {}", post_id, comment_id)
}

参数提取

查询参数 (#[q])

使用 #[q] 属性自动提取查询参数:

#[get("/search")]
async fn search(#[q] query: String, #[q] page: Option<i32>) -> String {
    format!("Search: {}, Page: {:?}", query, page)
}

// 支持元组模式
#[get("/search2")]
async fn search2(#[q] (query, page): (String, Option<i32>)) -> String {
    format!("Search: {}, Page: {:?}", query, page)
}

请求体 (#[b])

使用 #[b] 属性处理请求体,支持多种格式:

use serde::Deserialize;

#[derive(Deserialize)]
struct User {
    name: String,
    age: i32,
}

// JSON 格式(默认)
#[post("/users")]
async fn create_user(#[b] user: User) -> String {
    format!("Created user: {:?}", user)
}

// Form 格式
#[post("/users/form")]
async fn create_user_form(#[b(form)] user: User) -> String {
    format!("Created user from form: {:?}", user)
}

// Multipart 格式
#[post("/users/upload")]
async fn upload_user(#[b(multipart)] user: User) -> String {
    format!("Uploaded user: {:?}", user)
}

// 可选参数
#[post("/users/optional")]
async fn create_user_optional(#[b] user: Option<User>) -> String {
    match user {
        Some(u) => format!("Created user: {:?}", u),
        None => "No user provided".to_string(),
    }
}

配置

手动配置

use exum::config::ApplicationConfig;

let config = ApplicationConfig {
    addr: [127, 0, 0, 1],
    port: 3000,
};

let app = Application::build(config);

从配置文件加载

创建 config.toml 文件:

addr = [127, 0, 0, 1]

port = 3000

然后在代码中使用:

use exum::config::ApplicationConfig;

let config = ApplicationConfig::from_file("config.toml");
let app = Application::build(config);

环境特定配置

Exum 支持环境特定的配置文件覆盖。系统会自动检测当前环境并加载对应的配置文件:

  1. 环境检测规则

    • 如果设置了 EXUM_ENV 环境变量,则使用该值
    • 否则,在调试模式下使用 dev,生产模式下使用 prod
  2. 配置文件加载顺序

    • 首先加载 config.toml 作为基础配置
    • 然后加载 config.{env}.toml 并覆盖基础配置

示例:

# config.toml (基础配置)

addr = [127, 0, 0, 1]

port = 8080

database_url = "postgres://localhost:5432/mydb"

# config.dev.toml (开发环境配置)

port = 3000

database_url = "postgres://localhost:5432/mydb_dev"

# config.prod.toml (生产环境配置)

addr = [0, 0, 0, 0]

database_url = "${DATABASE_URL}"

环境变量注入

配置文件支持环境变量注入,使用 ${VARIABLE_NAME} 语法:

# config.toml

database_url = "${DATABASE_URL}"

api_key = "${API_KEY}"

port = "${PORT:-8080}"  # 支持默认值

在代码中自动加载配置:

use exum::config::ApplicationConfig;

// 自动加载配置,包含环境变量注入和环境特定配置
let config = ApplicationConfig::load();
let app = Application::build(config);

#[main] 宏

Exum 提供了 #[main] 宏来自动注入 Application 的构建和运行代码:

#[main]
async fn main() {
    // 这里可以添加自定义逻辑
    println!("服务器启动中...");
}

// 或者从配置文件加载配置
#[main(config = "config.toml")]
async fn main() {
    // 这里可以添加自定义逻辑
    println!("使用配置文件启动服务器...");
}

#[main] 宏会自动注入以下代码:

  • #[tokio::main] 属性
  • Application::build() 调用
  • app.run().await 调用

完整示例

use exum::*;
use serde::Deserialize;

#[derive(Deserialize, Debug)]
struct CreateUserRequest {
    name: String,
    email: String,
}

#[get("/users/:id")]
async fn get_user(id: String) -> String {
    format!("Getting user with ID: {}", id)
}

#[post("/users")]
async fn create_user(#[b] user: CreateUserRequest) -> String {
    format!("Creating user: {:?}", user)
}

#[put("/users/:id")]
async fn update_user(id: String, #[b] user: CreateUserRequest) -> String {
    format!("Updating user {}: {:?}", id, user)
}

#[delete("/users/:id")]
async fn delete_user(id: String) -> String {
    format!("Deleting user with ID: {}", id)
}

#[get("/search")]
async fn search(#[q] query: String, #[q] page: Option<i32>) -> String {
    format!("Searching for '{}' on page {:?}", query, page)
}

#[tokio::main]
async fn main() {
    let app = Application::build(ApplicationConfig::default());
    app.run().await;
}

Features

  • deref-app: 为 Application 实现 Deref trait,可以直接访问底层的 Router
  • full: 包含所有特性

许可证

MIT License