代码
rust
let user_id: i32 = match query.id.parse() {
Ok(v) => v,
Err(_) => return HttpResponse::Ok().json(""),
};
一、数据流转全景
浏览器 URL actix-web 解析 parse() 转换
───────────────────────────────── ────────────────────── ─────────────────
GET /getuser?id=5 query.id = "5" (String) → 5 (i32)
GET /getuser?id=abc query.id = "abc" (String) → Err (解析失败)
GET /getuser?id=-10 query.id = "-10" (String) → -10 (i32)
GET /getuser?id=99999999999 query.id = "9999.." → Err (超出i32范围)
为什么需要 parse()?
URL 查询参数在 HTTP 协议中全是字符串 。浏览器发送 ?id=5 时,5 是一个字符串 "5",不是整数 5。而 data_manager 的查询函数需要 i32 类型:
rust
pub fn get_user_by_id(&self, user_id: i32) -> Option<&User> { // ← 要求 i32
self.user_map.get(&user_id)
}
二、parse() 的工作原理
parse() 是 Rust 标准库中 str 类型的方法,定义在 FromStr trait 里:
rust
// 标准库定义(简化版)
pub trait FromStr: Sized {
type Err;
fn from_str(s: &str) -> Result<Self, Self::Err>;
}
// parse 方法签名
fn parse<F: FromStr>(&self) -> Result<F, F::Err>;
核心特点:类型由目标变量推断
rust
// parse() 返回什么类型,取决于你把它赋值给什么类型
let a: i32 = "42".parse().unwrap(); // → 42 (i32)
let b: f64 = "3.14".parse().unwrap(); // → 3.14 (f64)
let c: bool = "true".parse().unwrap(); // → true (bool)
let d: usize = "100".parse().unwrap(); // → 100 (usize)
let e: String = "hello".parse().unwrap(); // → "hello" (String,不是很有意义但合法)
在当前代码中,类型注解 let user_id: i32 告诉编译器 parse() 要解析成 i32。
三、详细例子
例子 1:正常请求(parse 成功)
浏览器请求:GET /getuser?id=42
actix_web 解析:
URL 查询字符串 "id=42"
↓ #[derive(Deserialize)] 自动反序列化
UserQuery { id: "42" } ← id 是 String 类型
代码执行:
query.id → "42"
"42".parse::<i32>() → Ok(42)
match Ok(42) {
Ok(v) => v, // ← 匹配这里,user_id = 42
Err(_) => ... // 不执行
}
↓ dm.get_user_by_id(42)
↓ 在 user_map 中查找 key=42
→ 找到用户 → 返回 User JSON
例子 2:非法请求(parse 失败)
浏览器请求:GET /getuser?id=hello
actix_web 解析:
UserQuery { id: "hello" }
代码执行:
query.id → "hello"
"hello".parse::<i32>() → Err(ParseIntError { kind: InvalidDigit })
match Err(ParseIntError) {
Ok(v) => ..., // 不执行
Err(_) => return HttpResponse::Ok().json(""), // ← 匹配这里,直接返回空字符串
}
→ 浏览器收到:"" (空的双引号 JSON)
→ 状态码:200 OK(但内容为空)
例子 3:溢出(parse 失败)
浏览器请求:GET /getuser?id=2147483648
"2147483648".parse::<i32>() → Err(ParseIntError { kind: Overflow })
// i32 最大值是 2147483647
→ 同样返回空 JSON ""
例子 4:带符号、带空格(常见边界情况)
rust
" 42 ".parse::<i32>() // Ok(42) ← parse 自动 trim 空白
"+42".parse::<i32>() // Ok(42) ← 正号合法
"-10".parse::<i32>() // Ok(-10) ← 负数合法
"4.2".parse::<i32>() // Err ← 小数点不合法(不是整数)
"".parse::<i32>() // Err ← 空字符串不合法
四、match 分支对比
rust
let user_id: i32 = match query.id.parse() {
Ok(v) => v, // ✅ 解析成功 → 取值
Err(_) => return HttpResponse::Ok().json(""), // ❌ 解析失败 → 提前返回空JSON
};
| parse() 结果 | 匹配分支 | 效果 |
|---|---|---|
Ok(5) |
Ok(v) => v |
user_id = 5,继续执行查询 |
Err(ParseIntError) |
Err(_) => return ... |
函数直接返回,response body 为 "" |
_ 是通配符,表示"不管错误具体是什么(非法字符、溢出、空字符串等),统一返回空 JSON"。
五、与其他语言对比
javascript
// JavaScript:隐式转换,不报错
const id = parseInt("hello"); // → NaN(不报错,悄悄失败)
python
# Python:抛出异常
id = int("hello") # → ValueError: invalid literal for int()
rust
// Rust:返回 Result,强制你处理失败情况
let id: i32 = "hello".parse(); // → Result<i32, ParseIntError>
// 编译器强制你必须 match / unwrap / ? ,不能忽略
六、完整调用链
HTTP GET /getuser?id=5
│
▼
actix_web 从 URL 提取查询参数
│
▼
#[derive(Deserialize)] 自动将 id=5 → UserQuery { id: "5".to_string() }
│
▼
web::Query<UserQuery> 提取到 query 变量
│
▼
query.id.parse::<i32>() ← 字符串 "5" → 整数 5
│ ┌─ Ok(5) → 继续
│ └─ Err(_) → 返回 "" (防御性编程)
▼
dm.get_user_by_id(5) ← 在 HashMap<i32, User> 中查找
│ ┌─ Some(user) → 序列化为 JSON 返回
│ └─ None → 返回 "" (用户不存在)
▼
HttpResponse::Ok().json(user.to_json())
│
▼
浏览器收到:
{
"userId": 5,
"ratings": [...],
"averageRating": 3.8,
...
}