动态路由 #
动态路由允许在URL路径中包含变量部分,使路由更加灵活。Rocket提供了强大的参数解析和类型转换功能。
路径参数 #
基本语法 #
使用 <param> 语法定义动态参数:
rust
#[get("/user/<id>")]
fn get_user(id: u32) -> String {
format!("User ID: {}", id)
}
单个参数 #
rust
#[get("/hello/<name>")]
fn hello(name: &str) -> String {
format!("Hello, {}!", name)
}
#[get("/age/<age>")]
fn age(age: u32) -> String {
format!("Age: {}", age)
}
#[get("/price/<amount>")]
fn price(amount: f64) -> String {
format!("Price: ${:.2}", amount)
}
多个参数 #
rust
#[get("/user/<user_id>/post/<post_id>")]
fn user_post(user_id: u32, post_id: u32) -> String {
format!("User {}'s post {}", user_id, post_id)
}
#[get("/category/<category>/product/<product>")]
fn product(category: &str, product: &str) -> String {
format!("Category: {}, Product: {}", category, product)
}
参数类型 #
基本类型 #
Rocket自动将路径参数转换为指定类型:
rust
#[get("/int/<num>")]
fn int_param(num: i32) -> String {
format!("Integer: {}", num)
}
#[get("/uint/<num>")]
fn uint_param(num: u32) -> String {
format!("Unsigned: {}", num)
}
#[get("/float/<num>")]
fn float_param(num: f64) -> String {
format!("Float: {}", num)
}
#[get("/bool/<val>")]
fn bool_param(val: bool) -> String {
format!("Boolean: {}", val)
}
字符串类型 #
rust
#[get("/string/<s>")]
fn string_param(s: String) -> String {
format!("String: {}", s)
}
#[get("/str/<s>")]
fn str_param(s: &str) -> String {
format!("&str: {}", s)
}
可选参数 #
rust
#[get("/maybe/<name>")]
fn maybe_name(name: Option<&str>) -> String {
match name {
Some(n) => format!("Hello, {}!", n),
None => "Hello, stranger!".to_string(),
}
}
Result类型 #
rust
use rocket::http::Status;
#[get("/user/<id>")]
fn get_user(id: Result<u32, &str>) -> Result<String, Status> {
match id {
Ok(id) => Ok(format!("User ID: {}", id)),
Err(_) => Err(Status::BadRequest),
}
}
多段路径参数 #
PathBuf类型 #
使用 <path..> 捕获多段路径:
rust
use std::path::PathBuf;
#[get("/file/<path..>")]
fn get_file(path: PathBuf) -> String {
format!("File path: {:?}", path)
}
访问 /file/docs/readme.md 返回 File path: "docs/readme.md"
访问 /file/a/b/c/d.txt 返回 File path: "a/b/c/d.txt"
结合静态路径 #
rust
#[get("/static/<path..>")]
fn static_files(path: PathBuf) -> String {
format!("Static file: {:?}", path)
}
#[get("/api/v1/<path..>")]
fn api_v1(path: PathBuf) -> String {
format!("API v1: {:?}", path)
}
查询参数 #
单个查询参数 #
rust
#[get("/search?<q>")]
fn search(q: &str) -> String {
format!("Searching for: {}", q)
}
访问 /search?q=rocket 返回 Searching for: rocket
多个查询参数 #
rust
#[get("/search?<q>&<page>")]
fn search_with_page(q: &str, page: u32) -> String {
format!("Searching '{}' on page {}", q, page)
}
结构化查询参数 #
rust
use rocket::request::Query;
use rocket::FromForm;
#[derive(FromForm)]
struct SearchParams {
q: String,
page: Option<usize>,
sort: Option<String>,
order: Option<String>,
}
#[get("/search?<params..>")]
fn search(params: Query<SearchParams>) -> String {
let page = params.page.unwrap_or(1);
let sort = params.sort.as_deref().unwrap_or("relevance");
let order = params.order.as_deref().unwrap_or("desc");
format!("Search '{}' - Page {}, Sort by {} {}", params.q, page, sort, order)
}
数组查询参数 #
rust
#[derive(FromForm)]
struct FilterParams {
#[field(name = "tag")]
tags: Vec<String>,
}
#[get("/filter?<params..>")]
fn filter(params: Query<FilterParams>) -> String {
format!("Tags: {:?}", params.tags)
}
访问 /filter?tag=rust&tag=web&tag=rocket 返回 Tags: ["rust", "web", "rocket"]
参数验证 #
类型验证 #
Rocket自动进行类型验证:
rust
#[get("/user/<id>")]
fn get_user(id: u32) -> String {
format!("User ID: {}", id)
}
访问 /user/abc 会返回404,因为 “abc” 不是有效的 u32。
自定义验证 #
rust
use rocket::request::{FromParam, Request};
use rocket::http::Status;
struct PositiveInt(u32);
impl<'a> FromParam<'a> for PositiveInt {
type Error = &'a str;
fn from_param(param: &'a str) -> Result<Self, Self::Error> {
let num: u32 = param.parse().map_err(|_| param)?;
if num > 0 {
Ok(PositiveInt(num))
} else {
Err(param)
}
}
}
#[get("/positive/<num>")]
fn positive(num: Result<PositiveInt, &str>) -> String {
match num {
Ok(PositiveInt(n)) => format!("Positive: {}", n),
Err(_) => "Must be a positive integer".to_string(),
}
}
枚举验证 #
rust
use rocket::request::FromParam;
enum Status {
Active,
Inactive,
Pending,
}
impl<'a> FromParam<'a> for Status {
type Error = &'a str;
fn from_param(param: &'a str) -> Result<Self, Self::Error> {
match param.to_lowercase().as_str() {
"active" => Ok(Status::Active),
"inactive" => Ok(Status::Inactive),
"pending" => Ok(Status::Pending),
_ => Err(param),
}
}
}
#[get("/status/<status>")]
fn get_status(status: Result<Status, &str>) -> String {
match status {
Ok(s) => format!("Status: {:?}", s),
Err(_) => "Invalid status".to_string(),
}
}
参数默认值 #
rust
#[derive(FromForm)]
struct PageParams {
#[field(default = 1)]
page: usize,
#[field(default = 10)]
per_page: usize,
}
#[get("/items?<params..>")]
fn list_items(params: Query<PageParams>) -> String {
format!("Page {} with {} items", params.page, params.per_page)
}
完整示例 #
rust
#[macro_use] extern crate rocket;
use rocket::request::Query;
use rocket::FromForm;
use rocket::serde::json::Json;
use rocket::serde::{Deserialize, Serialize};
use std::path::PathBuf;
#[derive(Serialize, Deserialize, FromForm)]
#[serde(crate = "rocket::serde")]
struct User {
id: u32,
name: String,
email: String,
}
#[derive(FromForm)]
struct SearchQuery {
q: String,
page: Option<usize>,
limit: Option<usize>,
}
#[get("/")]
fn index() -> &'static str {
"API Root"
}
#[get("/users/<id>")]
fn get_user(id: u32) -> Json<User> {
Json(User {
id,
name: "Alice".to_string(),
email: "alice@example.com".to_string(),
})
}
#[get("/users?<query..>")]
fn search_users(query: Query<SearchQuery>) -> String {
let page = query.page.unwrap_or(1);
let limit = query.limit.unwrap_or(10);
format!("Searching '{}' - Page {} (limit: {})", query.q, page, limit)
}
#[get("/files/<path..>")]
fn get_file(path: PathBuf) -> String {
format!("File: {:?}", path)
}
#[get("/user/<user_id>/posts/<post_id>")]
fn get_user_post(user_id: u32, post_id: u32) -> String {
format!("User {}'s post {}", user_id, post_id)
}
#[launch]
fn rocket() -> _ {
rocket::build()
.mount("/api", routes![
index,
get_user,
search_users,
get_file,
get_user_post
])
}
测试示例 #
bash
# 获取用户
curl http://127.0.0.1:8000/api/users/1
# 搜索用户
curl "http://127.0.0.1:8000/api/users?q=alice&page=1&limit=5"
# 获取文件
curl http://127.0.0.1:8000/api/files/docs/readme.md
# 获取用户文章
curl http://127.0.0.1:8000/api/user/1/posts/42
下一步 #
掌握了动态路由后,让我们继续学习 路由守卫,了解如何在路由级别进行权限控制。
最后更新:2026-03-28