-1

I am trying to read POSTed JSON using hyper 0.11.2. I don't see anything happening after "Reached" is printed.

fn call(&self, req: hyper::server::Request) -> Self::Future {
    let mut response: Response = Response::new();

    match (req.method(), req.path()) {
        (&Method::Post, "/assests") => {
             println!("Reached ... "); //POST: 200 OK
            //let (method, uri, _version, head 
            let mut res: Response = Response::new();
            if let Some(len) = req.headers().get::<ContentLength>() {
                res.headers_mut().set(len.clone());
            }

            println!("Reached xxx {:?}", req.body());
            res.with_body("req.body()");
        }
        _ => {
            response.set_status(StatusCode::NotFound);
        }
    };

    futures::future::ok(response)
}

Output:

Reached ...
Reached xxx Body(Body { [stream of values] })
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
BalaB
  • 3,687
  • 9
  • 36
  • 58
  • 1
    Please provide a [MCVE]. As it is, we have to guess what `Self::Future` is, among a number of other pieces of your application. – Shepmaster Sep 14 '17 at 19:57
  • Additionally, it would be good to explain what you don't understand from the [hyper examples which read a body stream](https://hyper.rs/guides/server/echo/), which seems like it should be an easy place to start from. – Shepmaster Sep 14 '17 at 19:59

3 Answers3

3

You created a new Response called response, then created a second Response called res. You then modify res and then throw it away, returning response from your function. If you return the thing you modify, your server returns the string "req.body()", just as you have specified.

fn call(&self, req: hyper::server::Request) -> Self::Future {
    let mut response: Response = Response::new();

    match (req.method(), req.path()) {
        (&Method::Post, "/assets") => {
            if let Some(len) = req.headers().get::<ContentLength>() {
                response.headers_mut().set(len.clone());
            }

            response = response.with_body("req.body()");
        }
        _ => {
            response.set_status(StatusCode::NotFound);
        }
    };

    futures::future::ok(response)
}

I would not set the content-length to an invalid value — your returned string does not match the length of the uploaded data.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • How to extract the body or its content in Method:POST ? I tried https://github.com/hyperium/hyper/blob/master/examples/server.rs But How to see the contents in req.body? chunk by chunk ? – BalaB Sep 14 '17 at 23:47
  • I don't quite see how this answers the question "How to read the request body in hyper?". – Philipp Ludwig Mar 20 '18 at 07:40
  • @PhilippLudwig good point. The original question was poorly framed and I've updated it. – Shepmaster Mar 20 '18 at 11:59
0
Below code works, able to parse JSON on POST


#[deny(warnings)]

use hyper::{Post, StatusCode};
use hyper::header::ContentLength;
use hyper::server::{Http, Service, Request, Response};
use futures::{future, Future, Stream}; 
use futures;
use hyper;
use serde_json;
use std::io;
use std::error::Error;


pub struct Persistence;

const SERVICE: &'static str = "Persistence Service";
const SUCCESS: &'static str = r#"{"status": "success"}"#;

impl Service for Persistence {
      type Request = Request;
    type Response = Response;
    type Error = hyper::Error;
    type Future = futures::BoxFuture<Response, hyper::Error>;

    fn call(&self, req: Request) -> Self::Future {
        let (method, uri, _version, headers, body) = req.deconstruct();
        match (method, uri.path()) {
            (Post, "/assests") => {
                let mut res = Response::new();
                let vec;
                if let Some(len) = headers.get::<ContentLength>() {
                    vec = Vec::with_capacity(**len as usize);
                    res.headers_mut().set(len.clone());
                } else {
                    vec = vec![];
                }
                body.fold(vec, |mut acc, chunk| {
                    acc.extend_from_slice(chunk.as_ref());
                    Ok::<_, hyper::Error>(acc)
                }).and_then(move |value| {
                     use serde_json;

                        let v: serde_json::Value = serde_json::from_slice(&value).map_err(|e| {
                            io::Error::new(
                                io::ErrorKind::Other,
                                e
                            )
                        })?;  
                    println!("value..... {:?}", &v);
                    Ok(res.with_body(SUCCESS))

                }).boxed()
            },
            _ => future::ok(Response::new().with_status(StatusCode::NotFound)).boxed()
        }
    }

}

THanks a lot @Shepmaster :) hip hip hurray!!

BalaB
  • 3,687
  • 9
  • 36
  • 58
  • 1
    If Shepmaster really helped you, would you consider attributing some value to his answer? I don't see a single upvote there. You may also want to explain the necessary changes, so that future visitors do not have to look through all the code to understand what you did. – E_net4 Sep 16 '17 at 21:01
  • https://stackoverflow.com/questions/43419974/how-do-i-read-the-entire-body-of-a-tokio-based-hyper-request – Gedweb Dec 09 '17 at 21:13
-1

according to https://hyper.rs/guides/server/handle_post/#handling-json-and-other-data-types

and here is the code:

extern crate futures;
extern crate hyper;
extern crate serde_json;

use futures::Future;
use futures::{Stream};

use hyper::{Get, Post, StatusCode};
use hyper::header::{ContentLength};
use hyper::server::{Http, Service, Request, Response};

static INDEX: &'static [u8] = b"Try POST /echo";

struct Echo;

impl Service for Echo {
    type Request = Request;
    type Response = Response;
    type Error = hyper::Error;
    type Future = Box<Future<Item = Self::Response, Error = Self::Error>>;

    fn call(&self, req: Request) -> Self::Future {
        match (req.method(), req.path()) {
            (&Get, "/") | (&Get, "/echo") => {
                Box::new(futures::future::ok(
                    Response::new()
                        .with_header(ContentLength(INDEX.len() as u64))
                        .with_body(INDEX)))
            },
            (&Post, "/echo") => {
        Box::new(req.body().concat2().map(|b| {
                    let bad_request: &[u8] = b"Missing field";
                    let json: serde_json::Value =
                        if let Ok(j) = serde_json::from_slice(b.as_ref()) {
                        j
                        } else {
                            return Response::new()
                                .with_status(StatusCode::BadRequest)
                                .with_header(ContentLength(bad_request.len() as u64))
                                .with_body(bad_request);
                    };

                    // use json as you like

                    let body = serde_json::to_string(&json).unwrap();
                    Response::new()
                        .with_header(ContentLength(body.len() as u64))
                        .with_body(body)
                }))
            },
            _ => {
                Box::new(futures::future::ok(
                    Response::new().with_status(StatusCode::NotFound)))
            }
        }
    }
}


fn main() {
    let addr = "127.0.0.1:1337".parse().unwrap();

    let server = Http::new().bind(&addr, || Ok(Echo)).unwrap();
    println!("Listening on http://{} with 1 thread.", server.local_addr().unwrap());
    server.run().unwrap();
}