路由属性 #
什么是路由属性? #
路由属性是 Actix Web 提供的宏,用于简化路由定义。通过属性宏,可以直接在处理函数上声明路由信息。
对比:传统方式 vs 属性宏 #
rust
// 传统方式
App::new()
.route("/users", web::get().to(list_users))
.route("/users/{id}", web::get().to(get_user))
.route("/users", web::post().to(create_user))
// 属性宏方式
App::new()
.service(list_users)
.service(get_user)
.service(create_user)
#[actix_web::get("/users")]
async fn list_users() -> impl Responder { ... }
#[actix_web::get("/users/{id}")]
async fn get_user() -> impl Responder { ... }
#[actix_web::post("/users")]
async fn create_user() -> impl Responder { ... }
HTTP 方法属性 #
GET 属性 #
rust
#[actix_web::get("/")]
async fn index() -> impl Responder {
HttpResponse::Ok().body("Home page")
}
#[actix_web::get("/users")]
async fn list_users() -> impl Responder {
HttpResponse::Ok().json(vec!["Alice", "Bob"])
}
#[actix_web::get("/users/{id}")]
async fn get_user(path: web::Path<u32>) -> impl Responder {
HttpResponse::Ok().body(format!("User ID: {}", path.into_inner()))
}
POST 属性 #
rust
#[derive(Deserialize)]
struct CreateUser {
name: String,
email: String,
}
#[actix_web::post("/users")]
async fn create_user(body: web::Json<CreateUser>) -> impl Responder {
HttpResponse::Created().json(serde_json::json!({
"id": 1,
"name": body.name,
"email": body.email
}))
}
PUT 属性 #
rust
#[derive(Deserialize)]
struct UpdateUser {
name: Option<String>,
email: Option<String>,
}
#[actix_web::put("/users/{id}")]
async fn update_user(
path: web::Path<u32>,
body: web::Json<UpdateUser>,
) -> impl Responder {
HttpResponse::Ok().json(serde_json::json!({
"id": path.into_inner(),
"updated": true
}))
}
DELETE 属性 #
rust
#[actix_web::delete("/users/{id}")]
async fn delete_user(path: web::Path<u32>) -> impl Responder {
HttpResponse::NoContent().finish()
}
PATCH 属性 #
rust
#[actix_web::patch("/users/{id}")]
async fn patch_user(
path: web::Path<u32>,
body: web::Json<UpdateUser>,
) -> impl Responder {
HttpResponse::Ok().json(serde_json::json!({
"id": path.into_inner(),
"patched": true
}))
}
HEAD 属性 #
rust
#[actix_web::head("/users/{id}")]
async fn head_user(path: web::Path<u32>) -> impl Responder {
HttpResponse::Ok()
.insert_header(("X-User-Id", path.into_inner().to_string()))
.insert_header(("Content-Length", "0"))
.finish()
}
OPTIONS 属性 #
rust
#[actix_web::options("/users")]
async fn options_users() -> impl Responder {
HttpResponse::Ok()
.insert_header(("Allow", "GET, POST, PUT, DELETE, PATCH"))
.insert_header(("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, PATCH"))
.finish()
}
路径参数 #
单个路径参数 #
rust
#[actix_web::get("/users/{id}")]
async fn get_user(path: web::Path<u32>) -> impl Responder {
let id = path.into_inner();
HttpResponse::Ok().json(serde_json::json!({ "id": id }))
}
多个路径参数 #
rust
#[derive(Deserialize)]
struct UserPostPath {
user_id: u32,
post_id: u32,
}
#[actix_web::get("/users/{user_id}/posts/{post_id}")]
async fn get_user_post(path: web::Path<UserPostPath>) -> impl Responder {
let params = path.into_inner();
HttpResponse::Ok().json(serde_json::json!({
"user_id": params.user_id,
"post_id": params.post_id
}))
}
路径参数类型 #
rust
#[actix_web::get("/int/{id}")]
async fn get_int(path: web::Path<i32>) -> impl Responder {
HttpResponse::Ok().body(format!("Integer: {}", path.into_inner()))
}
#[actix_web::get("/string/{name}")]
async fn get_string(path: web::Path<String>) -> impl Responder {
HttpResponse::Ok().body(format!("String: {}", path.into_inner()))
}
#[actix_web::get("/uuid/{id}")]
async fn get_uuid(path: web::Path<uuid::Uuid>) -> impl Responder {
HttpResponse::Ok().body(format!("UUID: {}", path.into_inner()))
}
路径参数约束 #
使用正则表达式 #
rust
#[actix_web::get("/users/{id:\\d+}")]
async fn get_user_by_id(path: web::Path<u32>) -> impl Responder {
HttpResponse::Ok().body(format!("User ID: {}", path.into_inner()))
}
#[actix_web::get("/files/{filename:[a-zA-Z0-9_-]+\\.[a-z]+}")]
async fn get_file(path: web::Path<String>) -> impl Responder {
HttpResponse::Ok().body(format!("File: {}", path.into_inner()))
}
自定义路径段 #
rust
#[actix_web::get("/users/{id:.*}")]
async fn get_user_any(path: web::Path<String>) -> impl Responder {
HttpResponse::Ok().body(format!("Path: {}", path.into_inner()))
}
查询参数 #
基本查询参数 #
rust
#[derive(Deserialize)]
struct QueryParams {
page: Option<u32>,
per_page: Option<u32>,
}
#[actix_web::get("/users")]
async fn list_users(query: web::Query<QueryParams>) -> impl Responder {
let page = query.page.unwrap_or(1);
let per_page = query.per_page.unwrap_or(10);
HttpResponse::Ok().json(serde_json::json!({
"page": page,
"per_page": per_page
}))
}
必需查询参数 #
rust
#[derive(Deserialize)]
struct SearchQuery {
q: String,
limit: Option<u32>,
}
#[actix_web::get("/search")]
async fn search(query: web::Query<SearchQuery>) -> impl Responder {
HttpResponse::Ok().json(serde_json::json!({
"query": query.q,
"limit": query.limit.unwrap_or(10)
}))
}
组合参数 #
rust
#[derive(Deserialize)]
struct UserId {
id: u32,
}
#[derive(Deserialize)]
struct UpdateQuery {
force: Option<bool>,
}
#[derive(Deserialize)]
struct UpdateBody {
name: String,
email: String,
}
#[actix_web::put("/users/{id}")]
async fn update_user(
path: web::Path<UserId>,
query: web::Query<UpdateQuery>,
body: web::Json<UpdateBody>,
) -> impl Responder {
let force = query.force.unwrap_or(false);
HttpResponse::Ok().json(serde_json::json!({
"id": path.id,
"force": force,
"name": body.name,
"email": body.email
}))
}
服务注册 #
使用 service 方法 #
rust
#[actix_web::get("/")]
async fn index() -> impl Responder {
HttpResponse::Ok().body("Index")
}
#[actix_web::get("/users")]
async fn users() -> impl Responder {
HttpResponse::Ok().body("Users")
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.service(index)
.service(users)
})
.bind("127.0.0.1:8080")?
.run()
.await
}
使用 configure #
rust
fn user_config(cfg: &mut web::ServiceConfig) {
cfg.service(list_users)
.service(get_user)
.service(create_user);
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.configure(user_config)
})
.bind("127.0.0.1:8080")?
.run()
.await
}
路由守卫 #
使用 guard 属性 #
rust
use actix_web::guard::{Guard, GuardContext};
struct AdminGuard;
impl Guard for AdminGuard {
fn check(&self, ctx: &GuardContext) -> bool {
ctx.head()
.headers()
.get("X-Admin")
.map(|v| v == "true")
.unwrap_or(false)
}
}
#[actix_web::get("/admin", guard = "AdminGuard")]
async fn admin_panel() -> impl Responder {
HttpResponse::Ok().body("Admin Panel")
}
使用 Header 守卫 #
rust
use actix_web::guard::Header;
#[actix_web::get("/api", guard = "Header(\"X-Api-Key\", \"secret\")")]
async fn api_endpoint() -> impl Responder {
HttpResponse::Ok().body("API Response")
}
路由中间件 #
在属性中指定中间件 #
rust
use actix_web::middleware::Logger;
#[actix_web::get("/logged", wrap = "Logger::default()")]
async fn logged_endpoint() -> impl Responder {
HttpResponse::Ok().body("This endpoint is logged")
}
完整 RESTful 示例 #
rust
use actix_web::{web, App, HttpResponse, HttpServer, Responder};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Clone)]
struct User {
id: u32,
name: String,
email: String,
}
#[derive(Deserialize)]
struct CreateUser {
name: String,
email: String,
}
#[derive(Deserialize)]
struct UpdateUser {
name: Option<String>,
email: Option<String>,
}
#[derive(Deserialize)]
struct ListQuery {
page: Option<u32>,
per_page: Option<u32>,
}
#[actix_web::get("/users")]
async fn list_users(query: web::Query<ListQuery>) -> impl Responder {
let page = query.page.unwrap_or(1);
let per_page = query.per_page.unwrap_or(10);
HttpResponse::Ok().json(serde_json::json!({
"page": page,
"per_page": per_page,
"users": [
{"id": 1, "name": "Alice", "email": "alice@example.com"},
{"id": 2, "name": "Bob", "email": "bob@example.com"}
]
}))
}
#[actix_web::get("/users/{id}")]
async fn get_user(path: web::Path<u32>) -> impl Responder {
let id = path.into_inner();
HttpResponse::Ok().json(User {
id,
name: "Alice".to_string(),
email: "alice@example.com".to_string(),
})
}
#[actix_web::post("/users")]
async fn create_user(body: web::Json<CreateUser>) -> impl Responder {
HttpResponse::Created().json(User {
id: 1,
name: body.name.clone(),
email: body.email.clone(),
})
}
#[actix_web::put("/users/{id}")]
async fn update_user(
path: web::Path<u32>,
body: web::Json<UpdateUser>,
) -> impl Responder {
let id = path.into_inner();
HttpResponse::Ok().json(User {
id,
name: body.name.clone().unwrap_or_default(),
email: body.email.clone().unwrap_or_default(),
})
}
#[actix_web::delete("/users/{id}")]
async fn delete_user(path: web::Path<u32>) -> impl Responder {
let id = path.into_inner();
HttpResponse::Ok().json(serde_json::json!({
"deleted": true,
"id": id
}))
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.service(list_users)
.service(get_user)
.service(create_user)
.service(update_user)
.service(delete_user)
})
.bind("127.0.0.1:8080")?
.run()
.await
}
属性宏总结 #
| 属性 | 说明 | 示例 |
|---|---|---|
#[get("/path")] |
GET 请求 | #[get("/users")] |
#[post("/path")] |
POST 请求 | #[post("/users")] |
#[put("/path")] |
PUT 请求 | #[put("/users/{id}")] |
#[delete("/path")] |
DELETE 请求 | #[delete("/users/{id}")] |
#[patch("/path")] |
PATCH 请求 | #[patch("/users/{id}")] |
#[head("/path")] |
HEAD 请求 | #[head("/users/{id}")] |
#[options("/path")] |
OPTIONS 请求 | #[options("/users")] |
guard = "..." |
路由守卫 | guard = "AdminGuard" |
wrap = "..." |
中间件包装 | wrap = "Logger::default()" |
下一步 #
现在你已经掌握了路由属性,继续学习 动态路由,深入了解动态路由和参数提取!
最后更新:2026-03-29