一、路由
上一篇文章,介绍了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

145

被折叠的 条评论
为什么被折叠?



