41 releases (7 breaking)

new 0.7.8 May 14, 2024
0.6.0 Apr 27, 2024
0.5.2 Mar 20, 2024
0.4.5 Dec 15, 2023
0.4.3 Nov 28, 2023

#130 in HTTP server

Download history 11/week @ 2024-02-26 321/week @ 2024-03-11 216/week @ 2024-03-18 240/week @ 2024-04-01 55/week @ 2024-04-08 129/week @ 2024-04-22 20/week @ 2024-04-29 794/week @ 2024-05-06

946 downloads per month
Used in hypers_middleware

Apache-2.0

235KB
6K SLoC

examples

hypers_admin

blog-rs

⚡️ Quick Start

Cargo.toml

## use hypers's full, openapi feature
[dependencies]
hypers = { version = "0.7", features = ["full","openapi"] }
tokio = { version = "=1.37.0", features = ["full"] }
serde = { version = "=1.0.201", features = ["derive"] }

Rust Code

use hypers::prelude::*;
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Extract, ToSchema)]
#[extract(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>,
    #[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 name: Option<String>,
    pub id: 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(
        "/header",
        tag = "Parse request header",
        responses(
            (status = 200, description = "header successfully", body = HeaderParam)
        ),
    )]
    pub fn header(input: HeaderParam) -> impl Responder {
        (200, Json(input))
    }
    // Uri Path Params
    #[delete(
        "/{id}/{name}/{firstName}",
        tag = "Parse request URL path params",
        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 name = {:?}\n id = {:?}\n first_name = {:?}",
                data.name, data.id, data.first_name
            ),
        )
    }
    // Uri Query Params
    #[get(
        "query",
        tag = "Parse request URL query params",
        params(Data),
        hook = [middleware::middleware_01], 
        responses(
            (status = 200, description = "query successfully", body = Data)
        )
    )]
    pub async fn query(data: Data) -> impl Responder {
        (200, Json(data))
    }
}

struct Api2;
#[openapi(name = "api2", components(schemas(Data)))]
impl Api2 {
    // Context-Type : application/x-www-form-urlencoded
    #[patch(
        "form",
        tag = "Submmit x-www-form-urlencoded data",
        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(data: Data) -> impl Responder {
        Ok::<_, Error>((200, Json(data)))
    }
    // Context-Type : multipart/form-data Form Fields
    #[post(
        "multipart_form",
        tag = "Submmit multipart/form-data Form Fields data",
        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, Json(form_data)))
    }
    // Context-Type : application/json
    #[post(
        "create",
        tag = "Submmit application/json data",
        request_body = Data,
        responses(
            (status = 200, description = "created successfully", body = Data),
            (status = 409, description = "Data already exists")
        )
    )]
    pub async fn create(data: Data) -> impl Responder {
        Ok::<_, Error>((200, Json(data)))
    }
}

struct Api3;
#[openapi(name = "api3", components(schemas(Data)))]
impl Api3 {
    #[delete(
    "/{id}/{name}",
    tag = "parse request url path params",
    params(
        ("id" = u16, Path, description = "Id 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")
    ))]
    pub fn delete(id: Path<u16>, name: Path<String>) -> impl Responder {
        (200, format!("id = {} name = {}", id.0, name.0))
    }

    // Url Query Params
    #[get(
    "/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")
    ))]
    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
    #[hook]
    pub async fn stat_time(req: Request, next: &mut 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)
    }
    #[hook]
    pub async fn middleware_01(req: Request, next: &mut Next<'_>) -> Result {
        if req.method() == "GET" {
            return next.next(req).await;
        }
        return Err(Error::Response(
            401,
            hypers::serde_json::json!("Bad request"),
        ));
    }

    #[hook]
    pub async fn middleware_02(req: Request, next: &mut Next<'_>) -> Result {
        if req.uri().path().starts_with("/api1") {
            return next.next(req).await;
        }
        return Err(Error::Response(
            401,
            hypers::serde_json::json!("Bad request"),
        ));
    }
}

#[tokio::main]
async fn main() -> Result<()> {
    let mut root = Router::default();
    root.push(Api1);
    root.push(Api2);
    root.push(Api3);
    root.swagger("swagger-ui", None); // Vist browser  http://127.0.0.1:7878/swagger-ui/
    println!("root router = {:#?}", root);
    let listener = hypers::TcpListener::bind("127.0.0.1:7878").await?;
    hypers::listen(root, listener).await
}

Dependencies

~14–30MB
~516K SLoC