响应类型 #
什么是响应器? #
响应器(Responder)是 Actix Web 中生成 HTTP 响应的核心 trait。任何实现了 Responder trait 的类型都可以作为处理函数的返回值。
text
┌─────────────────────────────────────────────────────────────┐
│ 响应器工作流程 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 处理函数返回值 │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Responder trait │ │
│ │ 实现类型: │ │
│ │ - HttpResponse │ │
│ │ - String / &'static str │ │
│ │ - web::Json<T> │ │
│ │ - web::Bytes │ │
│ │ - 自定义类型 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ HTTP 响应 │
│ Status: 200 OK │
│ Headers: Content-Type: ... │
│ Body: ... │
│ │
└─────────────────────────────────────────────────────────────┘
内置响应类型 #
HttpResponse #
rust
use actix_web::{HttpResponse, Responder};
#[actix_web::get("/")]
async fn index() -> impl Responder {
HttpResponse::Ok().body("Hello, World!")
}
字符串 #
rust
#[actix_web::get("/string")]
async fn string_response() -> impl Responder {
"Plain text response"
}
#[actix_web::get("/static")]
async fn static_response() -> impl Responder {
"Static string response"
}
JSON #
rust
use actix_web::web::Json;
use serde::Serialize;
#[derive(Serialize)]
struct User {
id: u32,
name: String,
}
#[actix_web::get("/user")]
async fn get_user() -> impl Responder {
Json(User {
id: 1,
name: "Alice".to_string(),
})
}
Bytes #
rust
use actix_web::web::Bytes;
#[actix_web::get("/bytes")]
async fn bytes_response() -> impl Responder {
Bytes::from_static(b"Raw bytes response")
}
HttpResponse 构建器 #
状态码 #
rust
use actix_web::HttpResponse;
#[actix_web::get("/status")]
async fn status_examples() -> impl Responder {
HttpResponse::Ok() // 200
HttpResponse::Created() // 201
HttpResponse::Accepted() // 202
HttpResponse::NoContent() // 204
HttpResponse::BadRequest() // 400
HttpResponse::Unauthorized() // 401
HttpResponse::Forbidden() // 403
HttpResponse::NotFound() // 404
HttpResponse::MethodNotAllowed() // 405
HttpResponse::Conflict() // 409
HttpResponse::InternalServerError() // 500
HttpResponse::ServiceUnavailable() // 503
}
自定义状态码 #
rust
use actix_web::http::StatusCode;
#[actix_web::get("/custom")]
async fn custom_status() -> impl Responder {
HttpResponse::build(StatusCode::from_u16(418).unwrap())
.body("I'm a teapot")
}
响应头 #
rust
use actix_web::http::header;
#[actix_web::get("/headers")]
async fn with_headers() -> impl Responder {
HttpResponse::Ok()
.insert_header(("X-Custom-Header", "value"))
.insert_header(("Content-Type", "text/plain"))
.append_header(("Set-Cookie", "session=abc123"))
.body("Response with headers")
}
响应体 #
rust
#[actix_web::get("/body")]
async fn body_types() -> impl Responder {
HttpResponse::Ok()
.body("String body")
}
#[actix_web::get("/json")]
async fn json_body() -> impl Responder {
HttpResponse::Ok()
.json(serde_json::json!({
"message": "JSON response"
}))
}
#[actix_web::get("/stream")]
async fn stream_body() -> impl Responder {
use actix_web::web::Bytes;
use futures::stream;
HttpResponse::Ok()
.streaming(stream::iter(vec![
Ok::<_, actix_web::Error>(Bytes::from("Chunk 1")),
Ok(Bytes::from("Chunk 2")),
]))
}
实现 Responder #
基本实现 #
rust
use actix_web::{HttpRequest, HttpResponse, Responder};
use serde::Serialize;
#[derive(Serialize)]
struct ApiResponse<T> {
success: bool,
data: T,
}
impl<T: Serialize> Responder for ApiResponse<T> {
type Body = actix_web::body::BoxBody;
fn respond_to(self, _req: &HttpRequest) -> HttpResponse<Self::Body> {
HttpResponse::Ok()
.json(&self)
.map_into_boxed_body()
}
}
#[actix_web::get("/api/user")]
async fn api_user() -> impl Responder {
ApiResponse {
success: true,
data: serde_json::json!({ "name": "Alice" }),
}
}
带错误处理的响应 #
rust
use actix_web::{HttpRequest, HttpResponse, Responder, http::StatusCode};
enum AppResult<T> {
Ok(T),
Err(String),
}
impl<T: Serialize> Responder for AppResult<T> {
type Body = actix_web::body::BoxBody;
fn respond_to(self, _req: &HttpRequest) -> HttpResponse<Self::Body> {
match self {
AppResult::Ok(data) => HttpResponse::Ok()
.json(serde_json::json!({
"success": true,
"data": data
}))
.map_into_boxed_body(),
AppResult::Err(msg) => HttpResponse::build(StatusCode::BAD_REQUEST)
.json(serde_json::json!({
"success": false,
"error": msg
}))
.map_into_boxed_body(),
}
}
}
#[actix_web::get("/result")]
async fn result_response() -> impl Responder {
AppResult::Ok(serde_json::json!({ "id": 1 }))
}
自定义响应类型 #
HTML 响应 #
rust
struct Html(String);
impl Responder for Html {
type Body = String;
fn respond_to(self, _req: &HttpRequest) -> HttpResponse<Self::Body> {
HttpResponse::Ok()
.content_type("text/html; charset=utf-8")
.body(self.0)
}
}
#[actix_web::get("/html")]
async fn html_response() -> impl Responder {
Html("<h1>Hello, HTML!</h1>".to_string())
}
XML 响应 #
rust
struct Xml(String);
impl Responder for Xml {
type Body = String;
fn respond_to(self, _req: &HttpRequest) -> HttpResponse<Self::Body> {
HttpResponse::Ok()
.content_type("application/xml")
.body(self.0)
}
}
#[actix_web::get("/xml")]
async fn xml_response() -> impl Responder {
Xml(r#"<?xml version="1.0"?><root><message>Hello</message></root>"#.to_string())
}
文件下载响应 #
rust
use actix_web::http::header::{ContentDisposition, DispositionParam, DispositionType};
struct FileDownload {
filename: String,
content: Vec<u8>,
}
impl Responder for FileDownload {
type Body = actix_web::body::BoxBody;
fn respond_to(self, _req: &HttpRequest) -> HttpResponse<Self::Body> {
HttpResponse::Ok()
.insert_header(ContentDisposition {
disposition: DispositionType::Attachment,
parameters: vec![DispositionParam::Filename(self.filename)],
})
.content_type("application/octet-stream")
.body(self.content)
.map_into_boxed_body()
}
}
#[actix_web::get("/download")]
async fn download_file() -> impl Responder {
FileDownload {
filename: "example.txt".to_string(),
content: b"File content".to_vec(),
}
}
响应构建器方法 #
链式调用 #
rust
#[actix_web::get("/chain")]
async fn chain_response() -> impl Responder {
HttpResponse::Ok()
.content_type("application/json")
.insert_header(("X-Request-Id", "12345"))
.insert_header(("X-Rate-Limit", "100"))
.json(serde_json::json!({
"status": "ok"
}))
}
Cookie 操作 #
rust
use actix_web::cookie::{Cookie, SameSite};
#[actix_web::get("/set-cookie")]
async fn set_cookie() -> impl Responder {
HttpResponse::Ok()
.cookie(
Cookie::build("session", "abc123")
.path("/")
.secure(true)
.http_only(true)
.same_site(SameSite::Strict)
.finish()
)
.body("Cookie set")
}
#[actix_web::get("/clear-cookie")]
async fn clear_cookie() -> impl Responder {
HttpResponse::Ok()
.cookie(
Cookie::build("session", "")
.path("/")
.max_age(actix_web::cookie::time::Duration::seconds(0))
.finish()
)
.body("Cookie cleared")
}
流式响应 #
流式输出 #
rust
use actix_web::web::Bytes;
use futures::stream::{self, StreamExt};
#[actix_web::get("/stream")]
async fn stream_response() -> impl Responder {
let stream = stream::iter(0..10)
.map(|n| Ok::<_, actix_web::Error>(Bytes::from(format!("Chunk {}\n", n))));
HttpResponse::Ok()
.content_type("text/plain")
.streaming(stream)
}
Server-Sent Events #
rust
use actix_web::web::Bytes;
use futures::stream::{Stream, StreamExt};
use std::time::Duration;
fn sse_stream() -> impl Stream<Item = Result<Bytes, actix_web::Error>> {
stream::iter(0..)
.map(|n| Ok(Bytes::from(format!("data: Message {}\n\n", n))))
.throttle(Duration::from_secs(1))
}
#[actix_web::get("/sse")]
async fn sse() -> impl Responder {
HttpResponse::Ok()
.content_type("text/event-stream")
.insert_header(("Cache-Control", "no-cache"))
.insert_header(("Connection", "keep-alive"))
.streaming(sse_stream())
}
重定向 #
rust
#[actix_web::get("/redirect")]
async fn redirect() -> impl Responder {
HttpResponse::Found()
.insert_header(("Location", "/target"))
.finish()
}
#[actix_web::get("/redirect-permanent")]
async fn redirect_permanent() -> impl Responder {
HttpResponse::MovedPermanently()
.insert_header(("Location", "/new-location"))
.finish()
}
完整示例 #
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(Serialize)]
struct ApiResponse<T> {
success: bool,
data: T,
}
impl<T: Serialize> Responder for ApiResponse<T> {
type Body = actix_web::body::BoxBody;
fn respond_to(self, _req: &HttpRequest) -> HttpResponse<Self::Body> {
HttpResponse::Ok()
.json(&self)
.map_into_boxed_body()
}
}
#[actix_web::get("/")]
async fn index() -> impl Responder {
HttpResponse::Ok()
.content_type("text/html")
.body("<h1>Welcome</h1>")
}
#[actix_web::get("/user/{id}")]
async fn get_user(path: web::Path<u32>) -> impl Responder {
ApiResponse {
success: true,
data: User {
id: path.into_inner(),
name: "Alice".to_string(),
email: "alice@example.com".to_string(),
},
}
}
#[actix_web::post("/users")]
async fn create_user(user: web::Json<User>) -> impl Responder {
HttpResponse::Created()
.json(ApiResponse {
success: true,
data: user.into_inner(),
})
}
#[actix_web::get("/redirect")]
async fn redirect_example() -> impl Responder {
HttpResponse::Found()
.insert_header(("Location", "/"))
.finish()
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.service(index)
.service(get_user)
.service(create_user)
.service(redirect_example)
})
.bind("127.0.0.1:8080")?
.run()
.await
}
下一步 #
现在你已经掌握了响应类型,继续学习 JSON 响应,深入了解 JSON 响应处理!
最后更新:2026-03-29