rustactix-web

Rust Actix Web Bytes Exractor Returns Empty Bytes (Wrongly)


I'm positive that a request body/payload in in a certain request (given that it is properly extracted and deserialized into JSON by actix_web) but the Bytes extracted by actix_web are empty. This only occurs when JSON is also extracted.

This is the function signature, in which I expect payload_bytes to be the byte representation of the request's payload.

#[post("/")]
pub async fn handle_root_request(
    event_data: web::Json<stripe::Event>,
    payload_bytes: web::Bytes,
    request: HttpRequest,
) -> Result<HttpResponse, Error>

However, payload_bytes comes out as empty within this function.

Minimal Reproducible Example:

use actix_web::*;

#[tokio::main]
async fn main() -> std::io::Result<()> {
    use std::net::{IpAddr, Ipv4Addr, SocketAddr}; // override default
                                                  //
    let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 3000);
    HttpServer::new(move || App::new().service(example))
        .bind(&addr)?
        .run()
        .await
}

#[post("/")]
pub async fn example(
    json_payload: web::Json<serde_json::Value>,
    payload_bytes: web::Bytes,
) -> Result<HttpResponse, Error> {
    println!("{}", payload_bytes.len());
    Ok(HttpResponse::Ok().into())
}

Run curl -X POST -H "Content-Type: application/json" -d '{"message":"hello world"}' http://localhost:3000 and watch the output.


Solution

  • You can extract the request body once and no more.

    However, you don't need to - you can just parse JSON from the Bytes:

    #[post("/")]
    pub async fn example(
        payload_bytes: web::Bytes,
    ) -> Result<HttpResponse, Error> {
        let json_payload = serde_json::from_slice::<serde_json::Value>(&payload_bytes)?;
    
        println!("{}", payload_bytes.len());
        Ok(HttpResponse::Ok().into())
    }