3

I'd like to have an Actix Web handler which responds to a POST request by printing the POST body to the console and constructing an HTTP response that contains the current URL from the request object.

When reading the request's POST body, futures seem to need to be involved. The closest I've gotten so far is:

fn handler(req: HttpRequest) -> FutureResponse<HttpResponse> {
    req.body()
        .from_err()
        .and_then(|bytes: Bytes| {
            println!("Body: {:?}", bytes);
            let url = format!("{scheme}://{host}",
                scheme = req.connection_info().scheme(),
                host = req.connection_info().host());
            Ok(HttpResponse::Ok().body(url).into())
        }).responder()
}

This won't compile because the future outlives the handler, so my attempts to read req.connection_info() are illegal. The compiler error suggests I use the move keyword into the closure definition, i.e. .and_then(move |bytes: Bytes| {. This also won't compile because req gets moved on the req.body() call and is then captured after the move in the references constructing url.

What is a reasonable way of constructing a scope in which I have access to data attached to the request object (e.g. the connection_info) at the same time as access to the POST body?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Mala
  • 14,178
  • 25
  • 88
  • 119
  • Please review how to create a [MCVE] and then [edit] your question to include it. We cannot tell what crates, types, traits, fields, etc. are present in the code. Ideally, produce something that reproduces your error on the [Rust Playground](https://play.rust-lang.org). There are [Rust-specific MCVE tips](//stackoverflow.com/tags/rust/info) as well. – Shepmaster Jul 07 '18 at 17:13
  • HttpReqauest implements Clone trait – Nikolay Kim Jul 08 '18 at 02:08

1 Answers1

4

The simplest solution is to not access it inside the future at all:

extern crate actix_web; // 0.6.14
extern crate bytes;     // 0.4.8
extern crate futures;   // 0.1.21

use actix_web::{AsyncResponder, FutureResponse, HttpMessage, HttpRequest, HttpResponse};
use bytes::Bytes;
use futures::future::Future;

fn handler(req: HttpRequest) -> FutureResponse<HttpResponse> {
    let url = format!(
        "{scheme}://{host}",
        scheme = req.connection_info().scheme(),
        host = req.connection_info().host(),
    );

    req.body()
        .from_err()
        .and_then(move |bytes: Bytes| {
            println!("Body: {:?}", bytes);
            Ok(HttpResponse::Ok().body(url).into())
        })
        .responder()
}

In case this is more than a quick hack for demonstration purposes, constructing URLs by concatenating strings is a terrible idea as it doesn't properly escape the values. You should be using a type that does that for you.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366