seanmonstar / warp

A super-easy, composable, web server framework for warp speeds.
https://seanmonstar.com/post/176530511587/warp
MIT License
9.59k stars 723 forks source link

HELP: filters inside .then never get executed #1094

Closed jarvjani closed 8 months ago

jarvjani commented 8 months ago

Sorry, i could not access discord, registration fails due to some server errors, so i created issue here

Version [dependencies] warp = {version = "0.3.3",features = ["tls"], default-features = false} tokio = { version = "1.24.2", features = ["full"] }

Platform Ubuntu 20.04 WSL2

Description I am trying to create a API with very few filters, since i experiences very slow build times when i implemented all the endpoints with own filters.Currently having +60 handlers in my project. Build times increased to upwards of 5 minutes. I am now using match to execute different handlers for endpoints that use similiar kind of http requests. The problem comes when i try to add any kind of new handler, the code gets increasinly more complex. I provided most simple example of my approach that doesn't work. The question is, how can i make this work?

Main.rs:

use std::collections::HashMap;

use warp::{self, Filter};
use warp::http::Method;
use warp::http::Response;

#[tokio::main]
async fn main() {

    let route  = warp::path("api")
    .and(warp::path::param())
    .and(warp::method())
    .then( |param:String,method| async move{
            match method{
                Method::GET=>{
                    match param.as_str(){
                        /* test command:
                         curl --verbose  http://localhost:8001/api/user
                         */
                        "user"=> {
                            return Response::builder()
                            .status(200)
                            .body(String::from("Heres your users"))
                        },
                        _=>{
                            return Response::builder()
                            .status(501)
                            .body(String::from("Not implemented"))
                        }
                    }
                },
                Method::POST=>{
                    match param.as_str(){
                        /* test command:
                         curl --verbose --data "{\"user_id\":\"123\",\"name\":\"john\"}"  http://localhost:8001/api/user
                         */
                        "user"=> {
                            warp::body::json()
                            .then(|json:HashMap<String,String>|async move{
                                /* why this one never gets executed? */
                                println!("{:?}",json);
                                return Response::builder()
                                .status(200)
                                .body(format!("inserting user!"))
                            });
                            return Response::builder()
                            .status(400)
                            .body(String::from("invalid request :("))
                        },
                        _=>{
                            return Response::builder()
                            .status(501)
                            .body(String::from("Not implemented"))
                        }
                    }
                }

                _=> return Response::builder()
                .status(400)
                .body(String::from("Method not supported"))
            }

        }
    );

    warp::serve(route)
    .run(([0, 0, 0, 0],8001)).await;
}

i don't understand how and why this works in the multipart.rs example:   
https://github.com/seanmonstar/warp/blob/master/examples/multipart.rs  

i want to do like in multipart.rs example, extract something from the request inside a filter:
https://github.com/seanmonstar/warp/blob/master/examples/multipart.rs

use bytes::BufMut;
use futures_util::TryStreamExt;
use warp::multipart::FormData;
use warp::Filter;

#[tokio::main]
async fn main() {
    // Running curl -F file=@.gitignore 'localhost:3030/' should print [("file", ".gitignore", "\n/target\n**/*.rs.bk\nCargo.lock\n.idea/\nwarp.iml\n")]
    let route = warp::multipart::form().and_then(|form: FormData| async move {
        let field_names: Vec<_> = form
            .and_then(|mut field| async move {
                let mut bytes: Vec<u8> = Vec::new();

                // field.data() only returns a piece of the content, you should call over it until it replies None
                while let Some(content) = field.data().await {
                    let content = content.unwrap();
                    bytes.put(content);
                }
                Ok((
                    field.name().to_string(),
                    field.filename().unwrap().to_string(),
                    String::from_utf8_lossy(&*bytes).to_string(),
                ))
            })
            .try_collect()
            .await
            .unwrap();

        Ok::<_, warp::Rejection>(format!("{:?}", field_names))
    });
    warp::serve(route).run(([127, 0, 0, 1], 3030)).await;
}

i don't understand how awaiting on the filter actually works, and why i cannot await on .then or .and_then? is it just FormData thing?

I run the curl command : curl --data "{\"user_id\":\"123\",\"name\":\"john\"}" http://localhost:8001/api/user I expected to see this happen:

inserting user!

Instead, this happened:

invalid request :(

jarvjani commented 8 months ago

Ended up using peek() to get the path parameter, so i don't have to do nested filters.