Skip to main content

Handlers

Writing asynchronous request handlers.

Actix 4.9+ 5 min read

# Handlers

A request handler is an asynchronous function that receives zero or more arguments (Extractors) and returns a type that implements the Responder trait.

##Handler Signature

A valid handler must be async and can take a maximum of 12 extractor arguments. (If you need more, you can bundle them into tuples).

code_blocksrust
async fn my_handler(
    path: web::Path<u32>,
    query: web::Query<MyQuery>,
    json: web::Json<MyPayload>,
) -> impl Responder {
    // ... logic ...
}

##Non-blocking I/O

Because Actix Web handles thousands of requests concurrently on a small number of threads, you must never block the thread inside a handler. Blocking operations include: - Synchronous file I/O (std::fs) - Synchronous network calls (e.g., using blocking reqwest instead of async) - Heavy CPU-bound computations without yielding

If you block a thread, no other requests assigned to that worker can be processed, leading to a massive drop in throughput and unresponsive servers.

If you must do a blocking operation (like using a legacy synchronous database driver), you should offload it to a thread pool using web::block:

code_blocksrust
use actix_web::{web, HttpResponse, Responder};

async fn blocking_handler() -> impl Responder {
    // web::block executes the closure on a separate thread pool
    // designed specifically for blocking tasks.
    let result = web::block(|| {
        // Do heavy CPU work or sync I/O here
        std::thread::sleep(std::time::Duration::from_secs(2));
        "Done!"
    })
    .await;

    match result {
        Ok(msg) => HttpResponse::Ok().body(msg),
        Err(_) => HttpResponse::InternalServerError().finish(),
    }
}

##Returning Custom Types

While you can always return HttpResponse, it is often cleaner to return your own domain types by implementing the Responder trait for them.

code_blocksrust
use actix_web::{body::BoxBody, http::header::ContentType, HttpRequest, HttpResponse, Responder};
use serde::Serialize;

#[derive(Serialize)]
struct MyObj {
    name: String,
}

// Implement Responder for MyObj
impl Responder for MyObj {
    type Body = BoxBody;

    fn respond_to(self, _req: &HttpRequest) -> HttpResponse<Self::Body> {
        let body = serde_json::to_string(&self).unwrap();
        HttpResponse::Ok()
            .content_type(ContentType::json())
            .body(body)
    }
}

With this trait implemented, you can now return MyObj directly from your handler function!

Was this page helpful?

Reference Implementation

The following code snippet demonstrates the authoritative pattern for this documentation.

src/main.rs
1use actix_web::{web, App, HttpServer, Responder};
2
3async fn async_handler(info: web::Path<String>) -> impl Responder {
4 format!("Hello, {}!", info)
5}
6
7#[actix_web::main]
8async fn main() -> std::io::Result<()> {
9 HttpServer::new(|| {
10 App::new().route("/{name}", web::get().to(async_handler))
11 })
12 .bind("127.0.0.1:8080")?
13 .run()
14 .await
15}
Was this page helpful?