hypers 0.1.2

A simple rust web framework based on hyper 1.0.0-rc.4
Documentation

HTTP Request URL Parameters Syntax

Pattern Kind Description
:name Normal Matches a path piece, excludes /
:name? Optional Matches an optional path piece, excludes /
/:name?/ /:name? OptionalSegment Matches an optional path segment, excludes /, prefix or suffix should be /
+ :name+ OneOrMore Matches a path piece, includes /
* :name* ZeroOrMore Matches an optional path piece, includes /
/*/ /* /:name*/ /:name* ZeroOrMoreSegment Matches zero or more path segments, prefix or suffix should be /

⚡️ Quick Start

use hypers's full feature

use hypers::prelude::*;
use std::time::Instant;

#[derive(Serialize, Deserialize)]
pub struct CookieJarParams {
    pub hypers: String,
    pub rust: String,
}

#[derive(Serialize, Deserialize)]
pub struct HeaderParams {
    pub host: Option<String>,
    #[serde(rename(deserialize = "user-agent"))]
    pub user_agent: Option<String>,
    pub accept: Option<String>,
}

#[derive(Serialize, Deserialize)]
pub struct PathParams {
    pub id: Option<u32>,
    pub name: Option<String>,
}

#[derive(Serialize, Deserialize)]
pub struct QueryParams {
    pub id: Option<u32>,
    pub name: Option<String>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct FormParams {
    pub id: u32,
    pub name: String,
    pub age: u16,
}

#[derive(Serialize, Deserialize)]
pub struct JsonParams {
    pub id: Option<u32>,
    pub name: Option<String>,
    pub age: Option<u16>,
}

// Request Cookies
pub async fn parse_cookies(req: Request) -> impl Responder {
    let cookie_jar = req.parse_cookies::<CookieJarParams>()?;
    Ok::<_, Error>((200, Json(cookie_jar)))
}

// Request Headers
pub async fn parse_header(req: Request) -> impl Responder {
    let header_params = req.parse_header::<HeaderParams>()?;
    /*
       ....
    */
    Ok::<_, Error>((200, Json(header_params)))
}

// Url Path Params
pub async fn parse_param(req: Request) -> impl Responder {
    let params = req.parse_param::<PathParams>()?;
    /*
       .....
    */
    Ok::<_, Error>((200, Json(params)))
}

// Url Query Params
pub async fn parse_query(req: Request) -> impl Responder {
    let query_params = req.parse_query::<QueryParams>()?;
    /*
       .....
    */
    Ok::<_, Error>((200, Form(query_params)))
}

// Context-Type : application/x-www-form-urlencoded
pub async fn parse_form_patch(mut req: Request) -> impl Responder {
    let user = req.parse::<FormParams>().await?;
    /*
       .....
    */
    Ok::<_, Error>((200, Json(user)))
}

// Context-Type : application/x-www-form-urlencoded
pub async fn parse_form_get(mut req: Request) -> impl Responder {
    let user = req.parse::<FormParams>().await?;
    /*
       .....
    */
    Ok::<_, Error>((200, Json(user)))
}

// Context-Type : application/json
pub async fn parse_json(mut req: Request) -> impl Responder {
    let user = req.parse::<JsonParams>().await?;
    /*
        ......
    */
    Ok::<_, Error>((200, Json(user)))
}

// Context-Type : multipart/form-data Form Fields
pub async fn multipart_form(mut req: Request) -> impl Responder {
    let form_data = req.parse::<FormParams>().await?;
    /*
       ....
    */
    Ok::<_, Error>((200, Json(form_data)))
}

// Context-Type : multipart/form-data Files
pub async fn multipart_file(mut req: Request) -> impl Responder {
    let file = req.file("file").await?;
    let file_name = file.name()?;
    let file_name = file_name.to_string();

    let img = req.files("imgs").await?;
    let imgs_name = img
        .iter()
        .map(|m| m.name().unwrap().to_string())
        .collect::<Vec<String>>()
        .join(",");

    Some((
        200,
        format!("file_name = {}, imgs_name = {}", file_name, imgs_name),
    ))
}

// Request Headers
pub async fn header(req: Request) -> impl Responder {
    let host = req.header::<String>("host")?;
    let user_agent = req.header::<String>("user-agent")?;
    /*
       .....
    */
    Some((
        200,
        format!("host = {} , user_agent = {}", host, user_agent),
    ))
}

// Url Path Params
pub async fn param(req: Request) -> impl Responder {
    let name = req.param::<String>("name")?;
    let age = req.param::<u16>("age")?;
    /*
       .....
    */
    Some((200, format!("name = {} , age = {}", name, age)))
}

// Url Query Params
pub async fn query(req: Request) -> impl Responder {
    let name = req.query::<Vec<String>>("name")?;
    let weapons = req.query::<u16>("weapons")?;
    /*
       .....
    */
    Some((200, format!("name = {:?} , weapons = {}", name, weapons)))
}

// Websocket  ws://127.0.0.1:7878/hello/ws  http://www.jsons.cn/websocket/
pub async fn websocket(req: Request, mut ws: WebSocket) -> Result<()> {
    let name = req.param::<String>("name").unwrap_or_default();
    while let Ok(msg) = ws.receive().await {
        if let Some(msg) = msg {
            match msg {
                Message::Text(text) => {
                    let text = format!("{},{}", name, text);
                    ws.send(Message::Text(text)).await?;
                }
                Message::Close(_) => break,
                _ => {}
            }
        }
    }
    Ok(())
}

// Middleware Function
pub async fn stat_time(req: Request, netx: Next) -> Result {
    // Before executing the request processing function
    let start_time = Instant::now();

    // Calling subsequent request processing functions
    let res = netx.next(req).await?;

    // After the execution of the request processing function
    let elapsed_time = start_time.elapsed();

    println!(
        "The current request processing function takes time :{:?}",
        elapsed_time
    );

    println!(
        "The response after the execution of the current request processing function : {:?}",
        res.body()
    );
    Ok(res)
}

pub async fn logger(req: Request, next: Next) -> Result {
    // Before executing the request processing function
    let uri = req.uri().path();
    println!("uri = {:?}", uri);

    // Calling subsequent request processing functions
    let res = next.next(req).await;

    // After the execution of the request processing function
    res
}

// Write router like a tree
fn main() -> Result<()> {
    
    // The Root Router
    let mut root = Router::new("/");

    // Add Middleware  ( Middleware can be added to any routing node )
    root.hook(logger, vec!["/param/:name/:age"], None);
    root.get("/*", StaticDir::new("src").listing(true));
    root.ws(":name/ws", websocket);
    root.get("/header", header);
    root.delete("param/:name/:age", param);
    root.get("query", query);
    root.get("set_cookies", |_| async {
        let mut cookie1 = Cookie::new("hypers", "hypers2023");
        cookie1.set_path("/user");

        let mut cookie2 = Cookie::new("rust", "athene2023");
        cookie2.set_path("/user");

        let mut cookie_jar = CookieJar::new();
        cookie_jar.add(cookie1);
        cookie_jar.add(cookie2);
        (200, cookie_jar)
    });

    // The Sub Router
    let mut user = Router::new("user");
    user.hook(stat_time, vec!["/user"], None);
    user.get("parse_cookies", parse_cookies)
        .get("/parse_header", parse_header)
        .delete("parse_param/:id/:name", parse_param)
        .get("parse_query", parse_query)
        .patch("parse_form_patch", parse_form_patch)
        .get("parse_form_get", parse_form_get)
        .post("parse_json", parse_json)
        .post("/multipart_form", multipart_form)
        .post("multipart_file", multipart_file);

    // Add sub router to the root router
    root.push(user);

    // Registering the root router into the server
    let app = hypers::new(root);

    // Start Server
    // Not Use SSL/TLS
    #[cfg(not(any(feature = "rustls", feature = "native_tls")))]
    {
        app.run("127.0.0.1:7878")
    }

    // Use SSL/TLS
    #[cfg(feature = "rustls")]
    {
        let tls = RustlsConfig::new()
            .cert(include_bytes!("./certs/cert.pem").to_vec())
            .key(include_bytes!("./certs/key.pem").to_vec());
        app.run("127.0.0.1:7878", tls)
    }

    // Use SSL/TLS
    #[cfg(feature = "native_tls")]
    {
        let tls = NativeTls::from_pkcs12(include_bytes!("./certs/identity.p12"), "mypass")?;
        app.run("127.0.0.1:7878", tls)
    }
}