Actix-Web路由
一、路由
上一篇文章,介绍了Actix-Web入门知识,链接如下:https://www.cnblogs.com/xiao987334176/p/19271995
接下来介绍路由
基础路由
修改主代码 src/main.rs
use actix_web::{get, post, put, delete, web, HttpResponse}; use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize)] struct User { id: u32, name: String, email: String, } // GET 请求 #[get("/users")] async fn get_users() -> HttpResponse { let users = vec![ User { id: 1, name: "Alice".to_string(), email: "alice@example.com".to_string(), }, User { id: 2, name: "Bob".to_string(), email: "bob@example.com".to_string(), }, ]; HttpResponse::Ok().json(users) } // GET 请求(带路径参数) #[get("/users/{id}")] async fn get_user(path: web::Path<u32>) -> HttpResponse { let user_id = path.into_inner(); let user = User { id: user_id, name: "Alice".to_string(), email: "alice@example.com".to_string(), }; HttpResponse::Ok().json(user) } // POST 请求(创建资源) #[post("/users")] async fn create_user(user: web::Json<User>) -> HttpResponse { println!("创建用户: {:?}", user.name); HttpResponse::Created().json(user.into_inner()) } // PUT 请求(更新资源) #[put("/users/{id}")] async fn update_user( path: web::Path<u32>, user: web::Json<User>, ) -> HttpResponse { let user_id = path.into_inner(); println!("更新用户 {}: {:?}", user_id, user.name); HttpResponse::Ok().json(user.into_inner()) } // DELETE 请求 #[delete("/users/{id}")] async fn delete_user(path: web::Path<u32>) -> HttpResponse { let user_id = path.into_inner(); println!("删除用户: {}", user_id); HttpResponse::NoContent().finish() }
以上是示例代码,看不出效果。接下来,将以上接口注册到Swagger
use actix_web::{delete, get, post, put, web, App, HttpResponse, HttpServer, Responder}; use serde::{Deserialize, Serialize}; use utoipa::{OpenApi, ToSchema}; use utoipa_swagger_ui::SwaggerUi; #[derive(Serialize, Deserialize, ToSchema, Clone, Debug)] struct User { id: u32, name: String, email: String, } /* ---------- hello ---------- */ #[derive(Serialize, ToSchema)] struct HelloReply { message: String, } /// 根路径接口 #[utoipa::path( get, path = "/", responses((status = 200, description = "Say hello", body = HelloReply)), tag = "hello" )] #[get("/")] async fn hello() -> impl Responder { web::Json(HelloReply { message: "Hello, world!".to_owned(), }) } /* ---------- user ---------- */ /// 列表用户 #[utoipa::path( get, path = "/users", responses((status = 200, description = "用户列表", body = Vec<User>)), tag = "user" )] #[get("/users")] async fn get_users() -> HttpResponse { HttpResponse::Ok().json(vec![ User { id: 1, name: "Alice".into(), email: "alice@example.com".into() }, User { id: 2, name: "Bob".into(), email: "bob@example.com".into() }, ]) } /// 根据 ID 查用户 #[utoipa::path( get, path = "/users/{id}", params(("id" = u32, Path, description = "用户主键")), responses((status = 200, description = "单个用户", body = User)), tag = "user" )] #[get("/users/{id}")] async fn get_user(id: web::Path<u32>) -> HttpResponse { HttpResponse::Ok().json(User { id: *id, name: "Alice".into(), email: "alice@example.com".into(), }) } /// 新建用户 #[utoipa::path( post, path = "/users", request_body = User, responses((status = 201, description = "创建成功", body = User)), tag = "user" )] #[post("/users")] async fn create_user(user: web::Json<User>) -> HttpResponse { HttpResponse::Created().json(user.into_inner()) } /// 更新用户 #[utoipa::path( put, path = "/users/{id}", params(("id" = u32, Path, description = "用户主键")), request_body = User, responses((status = 200, description = "更新成功", body = User)), tag = "user" )] #[put("/users/{id}")] async fn update_user(id: web::Path<u32>, user: web::Json<User>) -> HttpResponse { let _ = id.into_inner(); HttpResponse::Ok().json(user.into_inner()) } /// 删除用户 #[utoipa::path( delete, path = "/users/{id}", params(("id" = u32, Path, description = "用户主键")), responses((status = 204, description = "删除成功")), tag = "user" )] #[delete("/users/{id}")] async fn delete_user(id: web::Path<u32>) -> HttpResponse { let _ = id.into_inner(); HttpResponse::NoContent().finish() } /// OpenApi 文档 #[derive(OpenApi)] #[openapi( paths(hello, get_users, get_user, create_user, update_user, delete_user), components(schemas(HelloReply, User)), tags( (name = "hello", description = "hello接口"), (name = "user", description = "用户管理接口") ) )] struct ApiDoc; #[actix_web::main] async fn main() -> std::io::Result<()> { env_logger::init_from_env(env_logger::Env::default().default_filter_or("info")); log::info!("Starting HTTP server on http://127.0.0.1:8080"); HttpServer::new(|| { App::new() .service(hello) .service(get_users) .service(get_user) .service(create_user) .service(update_user) .service(delete_user) .service( SwaggerUi::new("/swagger-ui/{_:.*}") .url("/api-doc/openapi.json", ApiDoc::openapi()), ) }) .bind(("127.0.0.1", 8080))? .run() .await }
重新运行,访问Swagger UI:http://localhost:8080/swagger-ui/

这样就比较清晰了,接口做了分组显示。
先来测试列表用户,点击“Try it out”调试,可以看到返回了2条用户信息

查询参数与表单处理
修改主代码 src/main.rs
use actix_web::{web,get,post, HttpResponse,HttpServer,App}; use serde::Deserialize; #[derive(Deserialize)] struct QueryParams { page: Option<u32>, limit: Option<u32>, search: Option<String>, } // 查询参数 #[get("/search")] async fn search(query: web::Query<QueryParams>) -> HttpResponse { let page = query.page.unwrap_or(1); let limit = query.limit.unwrap_or(10); let search_term = query.search.as_deref().unwrap_or(""); HttpResponse::Ok().json(serde_json::json!({ "page": page, "limit": limit, "search": search_term, "results": [] })) } // 表单数据 #[derive(Deserialize)] struct LoginForm { username: String, password: String, } #[post("/login")] async fn login(form: web::Json<LoginForm>) -> HttpResponse { println!("登录用户: {}", form.username); // 模拟验证 if form.username == "admin" && form.password == "password" { HttpResponse::Ok().json(serde_json::json!({ "status": "success", "token": "mock-jwt-token" })) } else { HttpResponse::Unauthorized().json(serde_json::json!({ "status": "error", "message": "Invalid credentials" })) } } #[actix_web::main] async fn main() -> std::io::Result<()> { env_logger::init_from_env(env_logger::Env::default().default_filter_or("info")); log::info!("Starting HTTP server on http://127.0.0.1:8080"); HttpServer::new(|| { App::new() .service(search) .service(login) }) .bind(("127.0.0.1", 8080))? .run() .await }
重新运行,访问:http://127.0.0.1:8080/search
效果如下:

使用postman调用接口,http://127.0.0.1:8080/login
指定请求头类型为json

输出json参数
{ "username":"admin", "password":"password" }
请求接口,就可以得到json返回信息,效果如下

路由组织(Scope)
use actix_web::{web,get,post, HttpResponse,HttpServer,App}; use serde::Deserialize; use actix_web::web::Path; #[derive(Deserialize)] struct QueryParams { page: Option<u32>, limit: Option<u32>, search: Option<String>, } // 查询参数 #[get("/search")] async fn search(query: web::Query<QueryParams>) -> HttpResponse { let page = query.page.unwrap_or(1); let limit = query.limit.unwrap_or(10); let search_term = query.search.as_deref().unwrap_or(""); HttpResponse::Ok().json(serde_json::json!({ "page": page, "limit": limit, "search": search_term, "results": [] })) } // 表单数据 #[derive(Deserialize)] struct LoginForm { username: String, password: String, } #[post("/login")] async fn login(form: web::Json<LoginForm>) -> HttpResponse { println!("登录用户: {}", form.username); // 模拟验证 if form.username == "admin" && form.password == "password" { HttpResponse::Ok().json(serde_json::json!({ "status": "success", "token": "mock-jwt-token" })) } else { HttpResponse::Unauthorized().json(serde_json::json!({ "status": "error", "message": "Invalid credentials" })) } } #[actix_web::main] async fn main() -> std::io::Result<()> { env_logger::init_from_env(env_logger::Env::default().default_filter_or("info")); log::info!("Starting HTTP server on http://127.0.0.1:8080"); HttpServer::new(|| { App::new() // API v1 路由组 .service( web::scope("/api/v1") .service( web::scope("/users") .service(search) ) .service( web::scope("/posts") .route("", web::get().to(get_posts)) .route("/{id}", web::get().to(get_post)) ) ) // API v2 路由组 .service( web::scope("/api/v2") .service(search) ) }) .bind(("127.0.0.1", 8080))? .run() .await } async fn get_posts() -> HttpResponse { HttpResponse::Ok().json(Vec::<String>::new()) } async fn get_post(id: Path<PostId>) -> HttpResponse { println!("收到 post id: {}", id.id); // <-- 这里打印id HttpResponse::Ok().json(serde_json::json!({ "post_id": id.id })) } // 路径参数结构体 #[derive(serde::Deserialize)] struct PostId { id: String, // 如果想用 u32 也行,这里先用 String 更宽松 }
路由结构可视化:
/api/v1 ├── /users │ ├── GET / (获取用户列表) │ ├── GET /{id} (获取单个用户) │ ├── POST / (创建用户) │ ├── PUT /{id} (更新用户) │ └── DELETE /{id} (删除用户) └── /posts ├── GET / (获取文章列表) └── GET /{id} (获取单篇文章)
以上代码,可以访问以下几个接口
api v1
http://127.0.0.1:8080/api/v1/users/search

http://127.0.0.1:8080/api/v1/posts
输出:[]
http://127.0.0.1:8080/api/v1/posts/123
这里会打印出id参数

api v2
http://127.0.0.1:8080/api/v2/search


浙公网安备 33010602011771号