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