JSON 处理 #
JSON 简介 #
JSON 是现代 Web API 中最常用的数据交换格式。Actix Web 通过 Serde 库提供强大的 JSON 处理能力。
text
┌─────────────────────────────────────────────────────────────┐
│ JSON 处理流程 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 请求 JSON │
│ {"name": "Alice", "email": "alice@example.com"} │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ web::Json<T> 提取器 │ │
│ │ 反序列化 JSON → Rust 结构体 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 处理函数 │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ HttpResponse::Ok().json() │ │
│ │ 序列化 Rust 结构体 → JSON │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 响应 JSON │
│ {"id": 1, "name": "Alice", "email": "alice@example.com"} │
│ │
└─────────────────────────────────────────────────────────────┘
基本用法 #
JSON 请求体 #
rust
use actix_web::{web, HttpResponse, Responder};
use serde::{Deserialize, Serialize};
#[derive(Deserialize, Serialize)]
struct User {
name: String,
email: String,
}
#[actix_web::post("/users")]
async fn create_user(user: web::Json<User>) -> impl Responder {
HttpResponse::Created().json(user.into_inner())
}
JSON 响应 #
rust
#[actix_web::get("/users/{id}")]
async fn get_user(path: web::Path<u32>) -> impl Responder {
let user = User {
name: "Alice".to_string(),
email: "alice@example.com".to_string(),
};
HttpResponse::Ok().json(user)
}
Serde 属性 #
重命名字段 #
rust
#[derive(Deserialize, Serialize)]
struct User {
#[serde(rename = "userName")]
name: String,
#[serde(rename = "userEmail")]
email: String,
}
可选字段 #
rust
#[derive(Deserialize, Serialize)]
struct User {
name: String,
email: String,
#[serde(skip_serializing_if = "Option::is_none")]
phone: Option<String>,
#[serde(default)]
age: u32,
}
嵌套结构 #
rust
#[derive(Deserialize, Serialize)]
struct Address {
street: String,
city: String,
country: String,
}
#[derive(Deserialize, Serialize)]
struct User {
name: String,
email: String,
address: Address,
}
枚举类型 #
rust
#[derive(Deserialize, Serialize)]
#[serde(tag = "type")]
enum Role {
Admin { permissions: Vec<String> },
User { group: String },
Guest,
}
#[derive(Deserialize, Serialize)]
struct User {
name: String,
role: Role,
}
JSON 配置 #
大小限制 #
rust
use actix_web::web::JsonConfig;
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.app_data(JsonConfig::default()
.limit(1024 * 1024) // 1MB
)
.route("/users", web::post().to(create_user))
})
.bind("127.0.0.1:8080")?
.run()
.await
}
自定义错误处理 #
rust
use actix_web::error::JsonPayloadError;
fn json_error_handler(err: JsonPayloadError, _: &HttpRequest) -> actix_web::Error {
match err {
JsonPayloadError::ContentType => {
actix_web::error::ErrorBadRequest(serde_json::json!({
"error": "Invalid content type",
"expected": "application/json"
}))
}
JsonPayloadError::Payload(err) => {
actix_web::error::ErrorBadRequest(serde_json::json!({
"error": "Payload error",
"details": err.to_string()
}))
}
JsonPayloadError::Deserialize(err) => {
actix_web::error::ErrorBadRequest(serde_json::json!({
"error": "JSON deserialization failed",
"details": err.to_string()
}))
}
_ => actix_web::error::ErrorBadRequest(serde_json::json!({
"error": err.to_string()
}))
}
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.app_data(
JsonConfig::default()
.limit(4096)
.error_handler(json_error_handler)
)
.route("/users", web::post().to(create_user))
})
.bind("127.0.0.1:8080")?
.run()
.await
}
JSON 验证 #
使用 validator #
toml
[dependencies]
validator = { version = "0.16", features = ["derive"] }
rust
use validator::Validate;
#[derive(Deserialize, Serialize, Validate)]
struct CreateUser {
#[validate(length(min = 2, max = 100))]
name: String,
#[validate(email)]
email: String,
#[validate(range(min = 18, max = 120))]
age: Option<u32>,
}
#[actix_web::post("/users")]
async fn create_user(user: web::Json<CreateUser>) -> impl Responder {
if let Err(e) = user.validate() {
return HttpResponse::BadRequest().json(serde_json::json!({
"error": "Validation failed",
"details": e.to_string()
}));
}
HttpResponse::Created().json(serde_json::json!({
"id": 1,
"name": user.name,
"email": user.email
}))
}
自定义验证 #
rust
use serde::de::{self, Deserialize, Deserializer};
fn validate_password<'de, D>(deserializer: D) -> Result<String, D::Error>
where
D: Deserializer<'de>,
{
let password = String::deserialize(deserializer)?;
if password.len() < 8 {
return Err(de::Error::custom("Password must be at least 8 characters"));
}
if !password.chars().any(|c| c.is_numeric()) {
return Err(de::Error::custom("Password must contain at least one number"));
}
Ok(password)
}
#[derive(Deserialize, Serialize)]
struct RegisterUser {
name: String,
email: String,
#[serde(deserialize_with = "validate_password")]
password: String,
}
高级 JSON 操作 #
动态 JSON #
rust
use serde_json::Value;
#[actix_web::post("/dynamic")]
async fn dynamic_json(json: web::Json<Value>) -> impl Responder {
if let Some(name) = json.get("name").and_then(|v| v.as_str()) {
HttpResponse::Ok().json(serde_json::json!({
"greeting": format!("Hello, {}!", name)
}))
} else {
HttpResponse::BadRequest().json(serde_json::json!({
"error": "Missing 'name' field"
}))
}
}
原始 JSON 字符串 #
rust
#[actix_web::post("/raw")]
async fn raw_json(body: String) -> impl Responder {
match serde_json::from_str::<serde_json::Value>(&body) {
Ok(json) => HttpResponse::Ok().json(json),
Err(e) => HttpResponse::BadRequest().json(serde_json::json!({
"error": e.to_string()
}))
}
}
JSON Patch #
rust
use serde_json::json;
#[actix_web::patch("/users/{id}")]
async fn patch_user(
path: web::Path<u32>,
patch: web::Json<Value>,
) -> impl Responder {
let id = path.into_inner();
HttpResponse::Ok().json(serde_json::json!({
"id": id,
"patched": true,
"changes": patch
}))
}
批量操作 #
rust
#[derive(Deserialize, Serialize)]
struct BatchRequest<T> {
items: Vec<T>,
}
#[derive(Deserialize, Serialize)]
struct BatchResponse<T> {
created: Vec<T>,
errors: Vec<String>,
}
#[actix_web::post("/users/batch")]
async fn batch_create_users(
batch: web::Json<BatchRequest<CreateUser>>,
) -> impl Responder {
let mut created = Vec::new();
let mut errors = Vec::new();
for user in batch.items.iter() {
if user.name.is_empty() {
errors.push(format!("Empty name for user"));
} else {
created.push(User {
name: user.name.clone(),
email: user.email.clone(),
});
}
}
HttpResponse::Ok().json(BatchResponse { created, errors })
}
JSON API 示例 #
rust
use actix_web::{web, App, HttpResponse, HttpServer, Responder};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
#[derive(Debug, Clone, Serialize, Deserialize)]
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>,
}
struct AppState {
users: Mutex<HashMap<u32, User>>,
next_id: Mutex<u32>,
}
#[actix_web::get("/users")]
async fn list_users(state: web::Data<Arc<AppState>>) -> impl Responder {
let users = state.users.lock().unwrap();
let users: Vec<&User> = users.values().collect();
HttpResponse::Ok().json(users)
}
#[actix_web::get("/users/{id}")]
async fn get_user(
path: web::Path<u32>,
state: web::Data<Arc<AppState>>,
) -> impl Responder {
let users = state.users.lock().unwrap();
match users.get(&path.into_inner()) {
Some(user) => HttpResponse::Ok().json(user),
None => HttpResponse::NotFound().json(serde_json::json!({
"error": "User not found"
})),
}
}
#[actix_web::post("/users")]
async fn create_user(
body: web::Json<CreateUser>,
state: web::Data<Arc<AppState>>,
) -> impl Responder {
let mut next_id = state.next_id.lock().unwrap();
let id = *next_id;
*next_id += 1;
let user = User {
id,
name: body.name.clone(),
email: body.email.clone(),
};
state.users.lock().unwrap().insert(id, user.clone());
HttpResponse::Created().json(user)
}
#[actix_web::put("/users/{id}")]
async fn update_user(
path: web::Path<u32>,
body: web::Json<UpdateUser>,
state: web::Data<Arc<AppState>>,
) -> impl Responder {
let id = path.into_inner();
let mut users = state.users.lock().unwrap();
match users.get_mut(&id) {
Some(user) => {
if let Some(name) = &body.name {
user.name = name.clone();
}
if let Some(email) = &body.email {
user.email = email.clone();
}
HttpResponse::Ok().json(user.clone())
}
None => HttpResponse::NotFound().json(serde_json::json!({
"error": "User not found"
})),
}
}
#[actix_web::delete("/users/{id}")]
async fn delete_user(
path: web::Path<u32>,
state: web::Data<Arc<AppState>>,
) -> impl Responder {
let mut users = state.users.lock().unwrap();
match users.remove(&path.into_inner()) {
Some(_) => HttpResponse::NoContent().finish(),
None => HttpResponse::NotFound().json(serde_json::json!({
"error": "User not found"
})),
}
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let state = Arc::new(AppState {
users: Mutex::new(HashMap::new()),
next_id: Mutex::new(1),
});
HttpServer::new(move || {
App::new()
.app_data(web::Data::new(state.clone()))
.service(list_users)
.service(get_user)
.service(create_user)
.service(update_user)
.service(delete_user)
})
.bind("127.0.0.1:8080")?
.run()
.await
}
下一步 #
现在你已经掌握了 JSON 处理,继续学习 文件上传,深入了解文件处理!
最后更新:2026-03-29