salvo-routing

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Salvo Routing

Salvo 路由

This skill helps configure advanced routing patterns in Salvo applications.
本技能可帮助你在Salvo应用中配置高级路由模式。

Salvo Routing Innovation

Salvo 路由的创新特性

Salvo's routing system has unique features:
  1. Path as Filter: Path matching is essentially a filter, allowing unified combination with method and custom conditions
  2. Reusable Routes: Routers can be added to multiple locations for flexible composition
  3. Unified Middleware Model: Middleware and handlers share the same concept via
    hoop()
    method
  4. Flexible Nesting: Use
    push()
    for arbitrary depth hierarchical structures
Salvo的路由系统具备独特特性:
  1. 路径作为过滤器:路径匹配本质上是一种过滤器,可与请求方法、自定义条件统一组合
  2. 可复用路由:路由器可添加到多个位置,实现灵活组合
  3. 统一中间件模型:中间件与处理器通过
    hoop()
    方法共享同一概念
  4. 灵活嵌套:使用
    push()
    方法构建任意深度的层级结构

Path Patterns

路径模式

Static Paths

静态路径

rust
Router::with_path("users").get(list_users)
rust
Router::with_path("users").get(list_users)

Path Parameters

路径参数

Salvo uses
{id}
syntax for path parameters (since version 0.76). Earlier versions used
<id>
syntax, which is now deprecated.
rust
// Basic parameter
Router::with_path("users/{id}").get(show_user)

// Typed parameter (num, i32, i64, etc.)
Router::with_path("users/{id:num}").get(show_user)

// Regex pattern
Router::with_path(r"users/{id|\d+}").get(show_user)

// Wildcard (captures rest of path)
Router::with_path("files/{**path}").get(serve_file)
Salvo(从0.76版本开始)使用
{id}
语法定义路径参数。早期版本使用的
<id>
语法现已废弃。
rust
// 基础参数
Router::with_path("users/{id}").get(show_user)

// 类型化参数(num, i32, i64等)
Router::with_path("users/{id:num}").get(show_user)

// 正则表达式模式
Router::with_path(r"users/{id|\d+}").get(show_user)

// 通配符(捕获剩余路径)
Router::with_path("files/{**path}").get(serve_file)

Accessing Parameters

获取参数

rust
#[handler]
async fn show_user(req: &mut Request) -> String {
    let id = req.param::<i64>("id").unwrap();
    format!("User ID: {}", id)
}
rust
#[handler]
async fn show_user(req: &mut Request) -> String {
    let id = req.param::<i64>("id").unwrap();
    format!("User ID: {}", id)
}

Wildcard Types

通配符类型

Salvo supports multiple wildcard patterns (using
{}
syntax since version 0.76; earlier versions used
<>
syntax):
  1. {*}
    : Matches any single path segment
    rust
    Router::new().path("{*}").get(catch_all)
  2. {**}
    : Matches all remaining path segments (including slashes)
    rust
    Router::new().path("static/{**path}").get(serve_static)
    // Matches: static/css/style.css, static/js/main.js, etc.
  3. Named wildcards: Can retrieve matched content in handler
    rust
    Router::new().path("files/{*rest}").get(handler)
    // In handler: req.param::<String>("rest")
Salvo支持多种通配符模式(从0.76版本开始使用
{}
语法;早期版本使用
<>
语法):
  1. {*}
    :匹配任意单个路径段
    rust
    Router::new().path("{*}").get(catch_all)
  2. {**}
    :匹配所有剩余路径段(包括斜杠)
    rust
    Router::new().path("static/{**path}").get(serve_static)
    // 匹配:static/css/style.css, static/js/main.js等
  3. 命名通配符:可在处理器中获取匹配内容
    rust
    Router::new().path("files/{*rest}").get(handler)
    // 在处理器中:req.param::<String>("rest")

Nested Routers

嵌套路由器

Tree Structure

树形结构

rust
let router = Router::new()
    .push(
        Router::with_path("api/v1")
            .push(
                Router::with_path("users")
                    .get(list_users)
                    .post(create_user)
                    .push(
                        Router::with_path("{id}")
                            .get(show_user)
                            .patch(update_user)
                            .delete(delete_user)
                    )
            )
            .push(
                Router::with_path("posts")
                    .get(list_posts)
                    .post(create_post)
            )
    );
rust
let router = Router::new()
    .push(
        Router::with_path("api/v1")
            .push(
                Router::with_path("users")
                    .get(list_users)
                    .post(create_user)
                    .push(
                        Router::with_path("{id}")
                            .get(show_user)
                            .patch(update_user)
                            .delete(delete_user)
                    )
            )
            .push(
                Router::with_path("posts")
                    .get(list_posts)
                    .post(create_post)
            )
    );

Route Composition

路由组合

rust
fn user_routes() -> Router {
    Router::with_path("users")
        .get(list_users)
        .post(create_user)
        .push(
            Router::with_path("{id}")
                .get(get_user)
                .patch(update_user)
                .delete(delete_user)
        )
}

fn post_routes() -> Router {
    Router::with_path("posts")
        .get(list_posts)
        .post(create_post)
}

let api_v1 = Router::with_path("v1")
    .push(user_routes())
    .push(post_routes());

let api_v2 = Router::with_path("v2")
    .push(user_routes())
    .push(post_routes());

let router = Router::new()
    .push(Router::with_path("api/v1/users").get(list_users).post(create_user))
    .push(Router::with_path("api/v1/users/{id}").get(show_user).patch(update_user).delete(delete_user));
rust
fn user_routes() -> Router {
    Router::with_path("users")
        .get(list_users)
        .post(create_user)
        .push(
            Router::with_path("{id}")
                .get(get_user)
                .patch(update_user)
                .delete(delete_user)
        )
}

fn post_routes() -> Router {
    Router::with_path("posts")
        .get(list_posts)
        .post(create_post)
}

let api_v1 = Router::with_path("v1")
    .push(user_routes())
    .push(post_routes());

let api_v2 = Router::with_path("v2")
    .push(user_routes())
    .push(post_routes());

let router = Router::new()
    .push(Router::with_path("api/v1/users").get(list_users).post(create_user))
    .push(Router::with_path("api/v1/users/{id}").get(show_user).patch(update_user).delete(delete_user));

HTTP Methods

HTTP 请求方法

rust
Router::new()
    .get(handler)      // GET
    .post(handler)     // POST
    .put(handler)      // PUT
    .patch(handler)    // PATCH
    .delete(handler)   // DELETE
    .head(handler)     // HEAD
    .options(handler); // OPTIONS
rust
Router::new()
    .get(handler)      // GET
    .post(handler)     // POST
    .put(handler)      // PUT
    .patch(handler)    // PATCH
    .delete(handler)   // DELETE
    .head(handler)     // HEAD
    .options(handler); // OPTIONS

Path Matching Behavior

路径匹配机制

When a request arrives, routing works as follows:
  1. Filter Matching: First attempts to match route filters (path, method, etc.)
  2. Match Failed: If no filter matches, that route's middleware and handler are skipped
  3. Match Success: If matched, executes middleware and handler in order
rust
use salvo::routing::filters;

// Path filter
Router::with_filter(filters::path("users"))

// Method filter
Router::with_filter(filters::get())

// Combined filters
Router::with_filter(filters::path("users").and(filters::get()))
当请求到达时,路由匹配流程如下:
  1. 过滤器匹配:首先尝试匹配路由过滤器(路径、请求方法等)
  2. 匹配失败:若没有过滤器匹配,则跳过该路由的中间件与处理器
  3. 匹配成功:若匹配成功,则按顺序执行中间件与处理器
rust
use salvo::routing::filters;

// 路径过滤器
Router::with_filter(filters::path("users"))

// 请求方法过滤器
Router::with_filter(filters::get())

// 组合过滤器
Router::with_filter(filters::path("users").and(filters::get()))

Middleware with Routes

路由与中间件结合

Use
hoop()
to add middleware to routes:
rust
let router = Router::new()
    .hoop(logging)  // Applies to all routes
    .path("api")
    .push(
        Router::new()
            .hoop(auth_check)  // Only applies to routes under this
            .path("users")
            .get(list_users)
            .post(create_user)
    );
使用
hoop()
方法为路由添加中间件:
rust
let router = Router::new()
    .hoop(logging)  // 应用于所有路由
    .path("api")
    .push(
        Router::new()
            .hoop(auth_check)  // 仅应用于该路由下的所有子路由
            .path("users")
            .get(list_users)
            .post(create_user)
    );

HTTP Redirects

HTTP 重定向

rust
use salvo::prelude::*;
use salvo::writing::Redirect;

// Permanent redirect (301)
#[handler]
async fn permanent_redirect(res: &mut Response) {
    res.render(Redirect::permanent("/new-location"));
}

// Temporary redirect (302)
#[handler]
async fn temporary_redirect(res: &mut Response) {
    res.render(Redirect::found("/temporary-location"));
}

// See Other (303)
#[handler]
async fn see_other(res: &mut Response) {
    res.render(Redirect::see_other("/another-page"));
}
rust
use salvo::prelude::*;
use salvo::writing::Redirect;

// 永久重定向(301)
#[handler]
async fn permanent_redirect(res: &mut Response) {
    res.render(Redirect::permanent("/new-location"));
}

// 临时重定向(302)
#[handler]
async fn temporary_redirect(res: &mut Response) {
    res.render(Redirect::found("/temporary-location"));
}

// 查看其他地址(303)
#[handler]
async fn see_other(res: &mut Response) {
    res.render(Redirect::see_other("/another-page"));
}

Custom Route Filters

自定义路由过滤器

Create custom filters for complex matching logic:
rust
use salvo::prelude::*;
use salvo::routing::filter::Filter;
use uuid::Uuid;

pub struct GuidFilter;

impl Filter for GuidFilter {
    fn filter(&self, req: &mut Request, _state: &mut PathState) -> bool {
        if let Some(param) = req.param::<String>("id") {
            Uuid::parse_str(&param).is_ok()
        } else {
            false
        }
    }
}

#[handler]
async fn get_user_by_guid(req: &mut Request) -> String {
    let id = req.param::<Uuid>("id").unwrap();
    format!("User GUID: {}", id)
}

let router = Router::new()
    .path("users/{id}")
    .filter(GuidFilter)
    .get(get_user_by_guid);
创建自定义过滤器以实现复杂匹配逻辑:
rust
use salvo::prelude::*;
use salvo::routing::filter::Filter;
use uuid::Uuid;

pub struct GuidFilter;

impl Filter for GuidFilter {
    fn filter(&self, req: &mut Request, _state: &mut PathState) -> bool {
        if let Some(param) = req.param::<String>("id") {
            Uuid::parse_str(&param).is_ok()
        } else {
            false
        }
    }
}

#[handler]
async fn get_user_by_guid(req: &mut Request) -> String {
    let id = req.param::<Uuid>("id").unwrap();
    format!("User GUID: {}", id)
}

let router = Router::new()
    .path("users/{id}")
    .filter(GuidFilter)
    .get(get_user_by_guid);

Best Practices

最佳实践

  1. Use tree structure for complex APIs
  2. Use route composition functions for reusability
  3. Use regex constraints for path parameters when needed (
    {id:/\d+/}
    )
  4. Group related routes under common paths
  5. Use descriptive parameter names
  6. Apply middleware at the appropriate route level
  7. Prefer
    {id}
    syntax for consistency
  1. 为复杂API使用树形结构
  2. 使用路由组合函数提升复用性
  3. 必要时为路径参数添加正则约束(
    {id:/\d+/}
  4. 将相关路由分组到共同路径下
  5. 使用描述性的参数名称
  6. 在合适的路由层级应用中间件
  7. 优先使用
    {id}
    语法以保持一致性