How do I read an Iron Request in both middleware and the handler?

为君一笑 提交于 2020-01-03 16:46:12

问题


I'm working on a small API in Rust and am not sure how to access a Request from Iron in two places.

The Authentication middleware reads the Request once for a token and the actual route tries to read it again if the path is allowed (currently there is no check). This gives me an EOF error as the request has already been read.

I can't seem to easily clone the request and I believe it must be mutable in order to read the body.

extern crate iron;
extern crate router;
extern crate rustc_serialize;

use iron::prelude::*;
use iron::{BeforeMiddleware, status};
use router::Router;
use rustc_serialize::json;
use rustc_serialize::json::Json;
use std::io::Read;

#[derive(RustcEncodable, RustcDecodable)]
struct Greeting {
    msg: String
}

struct Authentication;

fn main() {
    let mut request_body = String::new();

    impl BeforeMiddleware for Authentication {
        fn before(&self, request: &mut Request) -> IronResult<()> {
            let mut payload = String::new();
            request.body.read_to_string(&mut payload).unwrap();
            let json = Json::from_str(&payload).unwrap();

            println!("json: {}", json);

            let token = json.as_object()
                .and_then(|obj| obj.get("token"))
                .and_then(|token| token.as_string())
                .unwrap_or_else(|| {
                    panic!("Unable to get token");
                });

            println!("token: {}", token);

            Ok(())
        }
    }

    fn attr(input: String, attribute: &str) -> String {
        let json = Json::from_str(&input).unwrap();
        let output = json.as_object()
            .and_then(|obj| obj.get(attribute))
            .and_then(|a| a.as_string())
            .unwrap_or_else(|| {
                panic!("Unable to get attribute {}", attribute);
            });

        String::from(output)
    }

    fn hello_world(_: &mut Request) -> IronResult<Response> {
        let greeting = Greeting { msg: "Hello, world!".to_string() };
        let payload = json::encode(&greeting).unwrap();
        Ok(Response::with((status::Ok, payload)))
    }

    // Receive a message by POST and play it back if auth-key is correct.
    fn set_greeting(request: &mut Request) -> IronResult<Response> {
        let mut payload = String::new();
        request.body.read_to_string(&mut payload).unwrap();
        let json = Json::from_str(&payload).unwrap();

        println!("json: {}", json);

        let msg = attr(payload, "msg");

        println!("msg: {}", msg);

        let greeting = Greeting { msg: String::from(msg) };
        let payload = json::encode(&greeting).unwrap();

        Ok(Response::with((status::Ok, payload)))
    }

    let mut router = Router::new();

    router.get("/", hello_world);
    router.post("/set", set_greeting);

    let mut chain = Chain::new(router);
    chain.link_before(Authentication);

    Iron::new(chain).http("localhost:3000").unwrap();
}

回答1:


Without knowing for sure, I don't think you can do anything to re-read the body (and you probably wouldn't want to for performance reasons). Instead, you could make your middleware parse the data and then store it in Request.extensions. Then your route would read it back out:

struct AuthenticatedBody;

impl iron::typemap::Key for AuthenticatedBody {
    type Value = Json;
}

struct Authentication;

impl BeforeMiddleware for Authentication {
    fn before(&self, request: &mut Request) -> IronResult<()> {
        let mut payload = String::new();
        request.body.read_to_string(&mut payload).unwrap();
        let json = Json::from_str(&payload).unwrap();

        {
            let token = json.as_object()
                .and_then(|obj| obj.get("token"))
                .and_then(|token| token.as_string())
                .unwrap_or_else(|| panic!("Unable to get token"));
        } // Scoped to end the borrow of `json`

        request.extensions.insert::<AuthenticatedBody>(json);

        Ok(())
    }
}

// ...

fn set_greeting(request: &mut Request) -> IronResult<Response> {
    let json = request.extensions.get::<AuthenticatedBody>().unwrap();
    // ...
}


来源:https://stackoverflow.com/questions/36435372/how-do-i-read-an-iron-request-in-both-middleware-and-the-handler

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!