31 releases (5 breaking)

0.5.4 Apr 7, 2024
0.5.2 Mar 20, 2024
0.4.5 Dec 15, 2023
0.4.3 Nov 28, 2023

#401 in HTTP server

Download history 8/week @ 2024-02-23 3/week @ 2024-03-01 86/week @ 2024-03-08 411/week @ 2024-03-15 40/week @ 2024-03-22 25/week @ 2024-03-29 259/week @ 2024-04-05 11/week @ 2024-04-12

357 downloads per month
Used in hypers_middleware

Apache-2.0

245KB
6.5K SLoC

Router Dependent on matchit

OpenApi Dependent on utoipa

⚡️ Quick Start

Cargo.toml

## use hypers's full feature
[dependencies]
hypers = { version = "=0.5.4", features = ["full","openapi"] }

Rust Code

use hypers::prelude::*;

#[derive(Serialize, Deserialize, Extract, ToSchema)]
#[extract(source("header"))]
pub struct HeaderParam {
    pub host: Option<String>,
    #[serde(rename = "user-agent")]
    pub user_agent: Option<String>,
    pub accept: Option<String>,
}

#[derive(Serialize, Deserialize, IntoParams, ToSchema, Extract)]
#[into_params(parameter_in = Query)]
pub struct Data {
    pub id: Option<u16>,
    pub name: Option<String>,
    pub age: Option<u16>,
    #[serde(rename = "firstName")]
    pub first_name: Option<String>,
    #[serde(rename = "lastName")]
    pub last_name: Option<String>,
}

#[derive(Serialize, Deserialize, IntoParams, Extract)]
pub struct PathData {
    pub email: Option<String>,
    pub age: Option<u16>,
    #[serde(rename = "firstName")]
    pub first_name: Option<String>,
}

struct Api1;
#[openapi(
    name = "api1", 
    hook = [middleware::stat_time],
    components(
        schemas(HeaderParam,Data)
    )
)]
impl Api1 {
    #[get(
        path("/header"),
        responses(
            (status = 200, description = "header successfully", body = HeaderParam)
        )
    )]
    pub fn header(input: HeaderParam) -> impl Responder {
        (200, input)
    }

    // Uri Path Params
    #[delete(
        path("/{email}/{age}/{firstName}"),
        hook = [middleware::stat_time],
        params(PathData),
        responses(
            (status = 200, description = "param successfully", body = String)
        )
    )]
    pub async fn param(data: PathData) -> impl Responder {
        (
            200,
            format!(
                "request url params:\n email = {:?}\n age = {:?}\n name = {:?}",
                data.email, data.age, data.first_name
            ),
        )
    }

    // Uri Query Params
    #[get(
        path("query"),
        params(Data),
        responses(
            (status = 200, description = "query successfully", body = Data)
        )
    )]
    pub async fn query(data: Data) -> impl Responder {
        (200, data)
    }
}

struct Api2;
#[openapi(
    name = "api2",
    components(
        schemas(Data)
    )
)]
impl Api2 {
    // Context-Type : application/x-www-form-urlencoded
    #[patch(
        path("form"),
        request_body(
            content = Data,
            content_type = "application/x-www-form-urlencoded",
        ),
        responses(
            (status = 200, description = "updated successfully", body = Data),
            (status = 409, description = "Data already exists")
        )
    )]
    pub async fn form(user: Data) -> impl Responder {
        Ok::<_, Error>((200, user))
    }

    // Context-Type : multipart/form-data Form Fields
    #[post(
        path("multipart_form"),
        request_body(
            content = Data,
            content_type = "multipart/form-data",
        ),
        responses(
            (status = 200, description = "updated successfully", body = Data),
            (status = 409, description = "Data already exists")
        )
    )]
    pub async fn multipart_form(form_data: Data) -> impl Responder {
        Ok::<_, Error>((200, form_data))
    }

    // Context-Type : application/json
    #[post(
        path("create"),
        request_body = Data,
        responses(
            (status = 200, description = "created successfully", body = Data),
            (status = 409, description = "Data already exists")
        )
    )]
    pub async fn create(user: Data) -> impl Responder {
        Ok::<_, Error>((200, user))
    }
}

struct Api3;
#[openapi(
    name = "api3",
    components(
        schemas(Data)
    )
)]
impl Api3 {

    #[delete(
    path("/{id}/{age}/{name}"),
    tag = "parse request url path params",
    params(
        ("id" = u16, Path, description = "Id of readme to delete"),
        ("age" = u16, Path, description = "Age of readme to delete"),
        ("name" = String, Path, description = "Name of readme to delete"),
    ),
    responses(
        (status = 200, description = "Parse Url Path Params successfully",body = Data),
        (status = 400, description = "Parse Url Path Params failed")
    ))]
    #[handler]
    pub fn delete(id: Path<u16>, age: Path<u16>, name: Path<String>) -> impl Responder {
        (
            200,
            Json(Data {
                id: Some(id.0),
                name: Some(name.0),
                age: Some(age.0),
                first_name: None,
                last_name: None,
            }),
        )
    }

    // Url Query Params
    #[get(
    path("/query_vec"),
    tag = "parse request url query params",
    params(
        ("name" = Vec<String>, Query, description = "Url Query Params name"),
        ("age" = u16, Query, description = "Url Query Params age"),
    ),
    responses(
        (status = 200, description = "successfully", body = String),
        (status = 400, description = "failed")
    ))]
    #[handler]
    pub fn query_vec(req: &mut Request) -> impl Responder {
        let name = req.query::<Vec<String>>("name")?;
        let age = req.query::<u16>("age")?;
        Some((200, format!("name = {:?} , age = {}", name, age)))
    }
}
mod middleware {
    use super::*;
    use std::time::Instant;

    // Middleware Function
    #[handler]
    pub async fn stat_time(req: &mut Request, next: &Next<'_>) -> Result {
        // Before executing the request processing function
        let start_time = Instant::now();

        // Calling subsequent request processing functions
        let res = next.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
        );
        Ok(res)
    }
}

fn main() -> Result<()> {
    
    // Vist browser  http://127.0.0.1:7878/swagger-ui/
    let swagger = SwaggerUi::new("swagger-ui").urls(vec![
        (Url::new("api1", "/api-docs/openapi1.json"), Api1.openapi()),
        (Url::new("api2", "/api-docs/openapi2.json"), Api2.openapi()),
        (Url::new("api3", "/api-docs/openapi3.json"), Api3.openapi()),
    ]);

    let mut root: Router = swagger.into();
    root.push(Api1.router());
    root.push(Api2.router());
    root.push(Api3.router());
    
    println!("root router = {:#?}", root);
    // Start Server
    hypers::run(root, "127.0.0.1:7878")
}

Dependencies

~16–33MB
~558K SLoC