JSON 响应 #
JSON 响应概述 #
JSON 是现代 Web API 的标准数据格式。Actix Web 提供了多种方式来生成 JSON 响应。
text
┌─────────────────────────────────────────────────────────────┐
│ JSON 响应方式 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. web::Json<T> │
│ 自动设置 Content-Type: application/json │
│ │
│ 2. HttpResponse::Ok().json() │
│ 手动构建响应并序列化为 JSON │
│ │
│ 3. serde_json::json!() │
│ 快速构建 JSON 值 │
│ │
│ 4. 自定义 Responder │
│ 完全控制响应格式 │
│ │
└─────────────────────────────────────────────────────────────┘
基本 JSON 响应 #
使用 web::Json #
rust
use actix_web::{web, HttpResponse, Responder};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
struct User {
id: u32,
name: String,
email: String,
}
#[actix_web::get("/user")]
async fn get_user() -> impl Responder {
web::Json(User {
id: 1,
name: "Alice".to_string(),
email: "alice@example.com".to_string(),
})
}
使用 HttpResponse::json #
rust
#[actix_web::get("/user/http")]
async fn get_user_http() -> impl Responder {
HttpResponse::Ok().json(User {
id: 1,
name: "Alice".to_string(),
email: "alice@example.com".to_string(),
})
}
使用 json! 宏 #
rust
#[actix_web::get("/json-macro")]
async fn json_macro() -> impl Responder {
HttpResponse::Ok().json(serde_json::json!({
"success": true,
"data": {
"id": 1,
"name": "Alice"
}
}))
}
标准化 API 响应 #
统一响应格式 #
rust
use serde::Serialize;
#[derive(Serialize)]
struct ApiResponse<T> {
success: bool,
code: u16,
message: String,
data: Option<T>,
}
impl<T: Serialize> ApiResponse<T> {
fn success(data: T) -> Self {
Self {
success: true,
code: 200,
message: "Success".to_string(),
data: Some(data),
}
}
fn error(code: u16, message: &str) -> ApiResponse<()> {
ApiResponse {
success: false,
code,
message: message.to_string(),
data: None,
}
}
}
#[actix_web::get("/users/{id}")]
async fn get_user(path: web::Path<u32>) -> impl Responder {
let user = User {
id: path.into_inner(),
name: "Alice".to_string(),
email: "alice@example.com".to_string(),
};
HttpResponse::Ok().json(ApiResponse::success(user))
}
分页响应 #
rust
#[derive(Serialize)]
struct PaginatedResponse<T> {
success: bool,
data: Vec<T>,
pagination: Pagination,
}
#[derive(Serialize)]
struct Pagination {
page: u32,
per_page: u32,
total: u64,
total_pages: u32,
}
#[actix_web::get("/users")]
async fn list_users(query: web::Query<PageQuery>) -> impl Responder {
let page = query.page.unwrap_or(1);
let per_page = query.per_page.unwrap_or(10);
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(PaginatedResponse {
success: true,
data: users,
pagination: Pagination {
page,
per_page,
total: 100,
total_pages: 10,
},
})
}
#[derive(Deserialize)]
struct PageQuery {
page: Option<u32>,
per_page: Option<u32>,
}
Serde 序列化配置 #
重命名字段 #
rust
#[derive(Serialize)]
struct User {
#[serde(rename = "userId")]
id: u32,
#[serde(rename = "userName")]
name: String,
#[serde(rename = "userEmail")]
email: String,
}
跳过空值 #
rust
#[derive(Serialize)]
struct User {
id: u32,
name: String,
#[serde(skip_serializing_if = "Option::is_none")]
email: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
phone: Option<String>,
}
格式化日期 #
rust
use chrono::{DateTime, Utc};
use serde_with::{serde_as, TimestampSeconds};
#[serde_as]
#[derive(Serialize)]
struct Event {
id: u32,
name: String,
#[serde_as(as = "TimestampSeconds<i64>")]
created_at: DateTime<Utc>,
}
枚举序列化 #
rust
#[derive(Serialize)]
#[serde(tag = "type", content = "value")]
enum Status {
Active,
Inactive { reason: String },
Pending,
}
#[derive(Serialize)]
struct User {
id: u32,
name: String,
status: Status,
}
自定义 JSON 响应 #
实现 Responder #
rust
use actix_web::{HttpRequest, HttpResponse, Responder};
struct JsonApiResponse<T> {
data: T,
}
impl<T: Serialize> Responder for JsonApiResponse<T> {
type Body = actix_web::body::BoxBody;
fn respond_to(self, _req: &HttpRequest) -> HttpResponse<Self::Body> {
HttpResponse::Ok()
.content_type("application/vnd.api+json")
.json(serde_json::json!({
"data": self.data
}))
.map_into_boxed_body()
}
}
#[actix_web::get("/api/user")]
async fn api_user() -> impl Responder {
JsonApiResponse {
data: User {
id: 1,
name: "Alice".to_string(),
email: "alice@example.com".to_string(),
},
}
}
错误响应 #
rust
#[derive(Serialize)]
struct ErrorResponse {
success: bool,
error: ErrorDetail,
}
#[derive(Serialize)]
struct ErrorDetail {
code: u16,
message: String,
#[serde(skip_serializing_if = "Option::is_none")]
details: Option<Vec<String>>,
}
impl ErrorResponse {
fn bad_request(message: &str) -> Self {
Self {
success: false,
error: ErrorDetail {
code: 400,
message: message.to_string(),
details: None,
},
}
}
fn validation_error(errors: Vec<String>) -> Self {
Self {
success: false,
error: ErrorDetail {
code: 422,
message: "Validation failed".to_string(),
details: Some(errors),
},
}
}
}
#[actix_web::post("/users")]
async fn create_user(body: web::Json<CreateUser>) -> impl Responder {
if body.name.is_empty() {
return HttpResponse::BadRequest()
.json(ErrorResponse::validation_error(vec![
"Name cannot be empty".to_string()
]));
}
HttpResponse::Created().json(ApiResponse::success(User {
id: 1,
name: body.name.clone(),
email: body.email.clone(),
}))
}
条件响应 #
根据请求头响应 #
rust
#[actix_web::get("/user/{id}")]
async fn get_user_conditional(
path: web::Path<u32>,
req: HttpRequest,
) -> impl Responder {
let user = User {
id: path.into_inner(),
name: "Alice".to_string(),
email: "alice@example.com".to_string(),
};
let accept = req.headers().get("Accept")
.and_then(|v| v.to_str().ok())
.unwrap_or("application/json");
if accept.contains("application/xml") {
HttpResponse::Ok()
.content_type("application/xml")
.body(format!(
r#"<?xml version="1.0"?><user><id>{}</id><name>{}</name></user>"#,
user.id, user.name
))
} else {
HttpResponse::Ok().json(user)
}
}
完整示例 #
rust
use actix_web::{web, App, HttpResponse, HttpServer, Responder};
use serde::{Deserialize, Serialize};
#[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>,
}
#[derive(Deserialize)]
struct ListQuery {
page: Option<u32>,
per_page: Option<u32>,
}
#[derive(Serialize)]
struct ApiResponse<T> {
success: bool,
data: T,
}
#[derive(Serialize)]
struct ListResponse<T> {
success: bool,
data: Vec<T>,
page: u32,
per_page: u32,
total: u64,
}
#[derive(Serialize)]
struct ErrorResponse {
success: bool,
error: String,
}
#[actix_web::get("/users")]
async fn list_users(query: web::Query<ListQuery>) -> impl Responder {
let page = query.page.unwrap_or(1);
let per_page = query.per_page.unwrap_or(10);
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(ListResponse {
success: true,
data: users,
page,
per_page,
total: 100,
})
}
#[actix_web::get("/users/{id}")]
async fn get_user(path: web::Path<u32>) -> impl Responder {
let id = path.into_inner();
if id > 100 {
return HttpResponse::NotFound().json(ErrorResponse {
success: false,
error: format!("User {} not found", id),
});
}
HttpResponse::Ok().json(ApiResponse {
success: true,
data: User {
id,
name: "Alice".to_string(),
email: "alice@example.com".to_string(),
},
})
}
#[actix_web::post("/users")]
async fn create_user(body: web::Json<CreateUser>) -> impl Responder {
if body.name.is_empty() {
return HttpResponse::BadRequest().json(ErrorResponse {
success: false,
error: "Name cannot be empty".to_string(),
});
}
HttpResponse::Created().json(ApiResponse {
success: true,
data: User {
id: 1,
name: body.name.clone(),
email: body.email.clone(),
},
})
}
#[actix_web::put("/users/{id}")]
async fn update_user(
path: web::Path<u32>,
body: web::Json<UpdateUser>,
) -> impl Responder {
HttpResponse::Ok().json(ApiResponse {
success: true,
data: User {
id: path.into_inner(),
name: body.name.clone().unwrap_or_default(),
email: body.email.clone().unwrap_or_default(),
},
})
}
#[actix_web::delete("/users/{id}")]
async fn delete_user(path: web::Path<u32>) -> impl Responder {
HttpResponse::Ok().json(ApiResponse {
success: true,
data: serde_json::json!({ "deleted": path.into_inner() }),
})
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.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