请求参数 #
什么是提取器? #
提取器(Extractor)是 Actix Web 中从请求中提取数据的组件。每个提取器都实现了 FromRequest trait,可以自动从请求中解析数据并传递给处理函数。
text
┌─────────────────────────────────────────────────────────────┐
│ 提取器工作流程 │
├─────────────────────────────────────────────────────────────┤
│ │
│ HTTP 请求 │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 提取器 │ │
│ │ 从请求中提取数据 │ │
│ │ - Path: 路径参数 │ │
│ │ - Query: 查询参数 │ │
│ │ - Json: JSON 请求体 │ │
│ │ - Form: 表单数据 │ │
│ │ - Header: 请求头 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 处理函数参数 │
│ │
└─────────────────────────────────────────────────────────────┘
内置提取器 #
Path - 路径参数 #
rust
use actix_web::{web, HttpResponse, Responder};
use serde::Deserialize;
#[derive(Deserialize)]
struct UserId {
id: u32,
}
#[actix_web::get("/users/{id}")]
async fn get_user(path: web::Path<UserId>) -> impl Responder {
HttpResponse::Ok().json(serde_json::json!({
"id": path.id
}))
}
Query - 查询参数 #
rust
#[derive(Deserialize)]
struct SearchQuery {
q: String,
page: Option<u32>,
limit: Option<u32>,
}
#[actix_web::get("/search")]
async fn search(query: web::Query<SearchQuery>) -> impl Responder {
HttpResponse::Ok().json(serde_json::json!({
"query": query.q,
"page": query.page.unwrap_or(1),
"limit": query.limit.unwrap_or(10)
}))
}
Json - JSON 请求体 #
rust
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())
}
Form - 表单数据 #
rust
#[derive(Deserialize)]
struct LoginForm {
username: String,
password: String,
}
#[actix_web::post("/login")]
async fn login(form: web::Form<LoginForm>) -> impl Responder {
HttpResponse::Ok().json(serde_json::json!({
"username": form.username,
"success": true
}))
}
Bytes - 原始字节 #
rust
#[actix_web::post("/raw")]
async fn raw_body(body: web::Bytes) -> impl Responder {
HttpResponse::Ok()
.content_type("application/octet-stream")
.body(body)
}
String - 字符串请求体 #
rust
#[actix_web::post("/text")]
async fn text_body(body: String) -> impl Responder {
HttpResponse::Ok()
.content_type("text/plain")
.body(format!("Received: {}", body))
}
高级提取器 #
HttpRequest - 原始请求 #
rust
use actix_web::HttpRequest;
#[actix_web::get("/info")]
async fn request_info(req: HttpRequest) -> impl Responder {
HttpResponse::Ok().json(serde_json::json!({
"method": req.method().as_str(),
"path": req.path(),
"query": req.query_string(),
"headers": req.headers().iter().map(|(k, v)| (k.to_string(), v.to_str().unwrap_or(""))).collect::<Vec<_>>()
}))
}
Header - 请求头 #
rust
use actix_web::http::header;
#[actix_web::get("/headers")]
async fn headers(req: HttpRequest) -> impl Responder {
let content_type = req.headers().get(header::CONTENT_TYPE);
let user_agent = req.headers().get(header::USER_AGENT);
let authorization = req.headers().get(header::AUTHORIZATION);
HttpResponse::Ok().json(serde_json::json!({
"content_type": content_type.map(|v| v.to_str().unwrap_or("")),
"user_agent": user_agent.map(|v| v.to_str().unwrap_or("")),
"authorization": authorization.map(|v| v.to_str().unwrap_or(""))
}))
}
Cookie - Cookie 提取 #
rust
use actix_web::cookie::Cookie;
#[actix_web::get("/cookie")]
async fn get_cookie(req: HttpRequest) -> impl Responder {
let cookie = req.cookie("session");
HttpResponse::Ok().json(serde_json::json!({
"session": cookie.map(|c| c.value())
}))
}
#[actix_web::post("/cookie")]
async fn set_cookie() -> impl Responder {
let cookie = Cookie::build("session", "abc123")
.path("/")
.secure(true)
.http_only(true)
.finish();
HttpResponse::Ok()
.cookie(cookie)
.body("Cookie set")
}
Payload - 流式请求体 #
rust
use actix_web::web::Payload;
use futures::StreamExt;
#[actix_web::post("/stream")]
async fn stream_body(mut payload: Payload) -> impl Responder {
let mut bytes = Vec::new();
while let Some(item) = payload.next().await {
bytes.extend_from_slice(&item.unwrap());
}
HttpResponse::Ok().body(bytes)
}
组合提取器 #
多个提取器 #
rust
#[derive(Deserialize)]
struct UserPath {
id: u32,
}
#[derive(Deserialize)]
struct UserQuery {
verbose: Option<bool>,
}
#[derive(Deserialize)]
struct UpdateUser {
name: Option<String>,
email: Option<String>,
}
#[actix_web::put("/users/{id}")]
async fn update_user(
path: web::Path<UserPath>,
query: web::Query<UserQuery>,
body: web::Json<UpdateUser>,
req: HttpRequest,
) -> impl Responder {
HttpResponse::Ok().json(serde_json::json!({
"id": path.id,
"verbose": query.verbose.unwrap_or(false),
"name": body.name,
"email": body.email,
"content_type": req.headers().get("content-type").and_then(|v| v.to_str().ok())
}))
}
自定义提取器 #
实现 FromRequest #
rust
use actix_web::{dev::Payload, Error, FromRequest, HttpRequest};
use futures::future::{ready, Ready};
struct ApiKey(String);
impl FromRequest for ApiKey {
type Error = Error;
type Future = Ready<Result<Self, Self::Error>>;
fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
match req.headers().get("X-Api-Key") {
Some(key) => ready(Ok(ApiKey(key.to_str().unwrap_or("").to_string()))),
None => ready(Err(actix_web::error::ErrorBadRequest("Missing API key"))),
}
}
}
#[actix_web::get("/protected")]
async fn protected(api_key: ApiKey) -> impl Responder {
HttpResponse::Ok().body(format!("API Key: {}", api_key.0))
}
带状态的自定义提取器 #
rust
use actix_web::web::Data;
use std::sync::Arc;
struct AppState {
valid_api_keys: Vec<String>,
}
struct AuthenticatedUser {
user_id: String,
}
impl FromRequest for AuthenticatedUser {
type Error = Error;
type Future = Ready<Result<Self, Self::Error>>;
fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
let state = req.app_data::<Data<Arc<AppState>>>();
match state {
Some(state) => {
let key = req.headers().get("X-Api-Key")
.and_then(|v| v.to_str().ok())
.unwrap_or("");
if state.valid_api_keys.contains(&key.to_string()) {
ready(Ok(AuthenticatedUser {
user_id: "user_123".to_string(),
}))
} else {
ready(Err(actix_web::error::ErrorUnauthorized("Invalid API key")))
}
}
None => ready(Err(actix_web::error::ErrorInternalServerError("State not found"))),
}
}
}
提取器配置 #
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(4096)
.error_handler(|err, _| {
actix_web::error::ErrorBadRequest(serde_json::json!({
"error": err.to_string()
}))
})
)
.route("/users", web::post().to(create_user))
})
.bind("127.0.0.1:8080")?
.run()
.await
}
Query 配置 #
rust
use actix_web::web::QueryConfig;
App::new()
.app_data(QueryConfig::default()
.error_handler(|err, _| {
actix_web::error::ErrorBadRequest(serde_json::json!({
"error": "Invalid query parameters",
"details": err.to_string()
}))
})
)
提取器总结 #
| 提取器 | 说明 | 用途 |
|---|---|---|
web::Path<T> |
路径参数 | /users/{id} |
web::Query<T> |
查询参数 | ?page=1&limit=10 |
web::Json<T> |
JSON 请求体 | API 请求 |
web::Form<T> |
表单数据 | 表单提交 |
web::Bytes |
原始字节 | 文件上传 |
String |
字符串请求体 | 文本处理 |
HttpRequest |
原始请求 | 获取完整请求信息 |
web::Payload |
流式请求体 | 大文件上传 |
| 自定义提取器 | 自定义逻辑 | 认证、验证等 |
完整示例 #
rust
use actix_web::{web, App, HttpResponse, HttpServer, Responder, HttpRequest};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
struct User {
id: u32,
name: String,
email: String,
}
#[derive(Deserialize)]
struct UserId {
id: u32,
}
#[derive(Deserialize)]
struct UserQuery {
fields: Option<String>,
}
#[derive(Deserialize)]
struct ListQuery {
page: Option<u32>,
per_page: Option<u32>,
sort: Option<String>,
}
#[derive(Deserialize)]
struct CreateUser {
name: String,
email: String,
}
#[derive(Deserialize)]
struct UpdateUser {
name: Option<String>,
email: Option<String>,
}
#[actix_web::get("/users")]
async fn list_users(query: web::Query<ListQuery>) -> impl Responder {
HttpResponse::Ok().json(serde_json::json!({
"page": query.page.unwrap_or(1),
"per_page": query.per_page.unwrap_or(10),
"sort": query.sort.as_deref().unwrap_or("id"),
"users": []
}))
}
#[actix_web::get("/users/{id}")]
async fn get_user(
path: web::Path<UserId>,
query: web::Query<UserQuery>,
req: HttpRequest,
) -> impl Responder {
HttpResponse::Ok().json(User {
id: path.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 {
HttpResponse::Created().json(User {
id: 1,
name: body.name.clone(),
email: body.email.clone(),
})
}
#[actix_web::put("/users/{id}")]
async fn update_user(
path: web::Path<UserId>,
body: web::Json<UpdateUser>,
) -> impl Responder {
HttpResponse::Ok().json(User {
id: path.id,
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<UserId>) -> impl Responder {
HttpResponse::NoContent().finish()
}
#[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
}
下一步 #
现在你已经掌握了请求参数提取,继续学习 表单数据,深入了解表单处理!
最后更新:2026-03-29