example

Useage

````rust use askama::Template; use athene::prelude::*; use athene::{html, json, status}; use headers::authorization::Bearer; use headers::Authorization; use serde::{Deserialize, Serialize}; use tracing::info;

use validator::{Validate, ValidationError};

[derive(Serialize, Deserialize, Clone, Validate)]

pub struct User { #[validate(email)] pub username: String, #[validate(range(min = 18, max = 20))] pub age: u16, #[validate(custom = "validateuserrole")] pub role: Option, }

fn validateuserrole(role: &str) -> Result<(), ValidationError> { if role.ne("student") { return Err(ValidationError::new("invalid role")); } Ok(()) }

// ================================ Basic Controller ===================================

[derive(Debug, Default, Deserialize, Serialize)]

pub struct AtheneController { pub label: String, pub keyword: String, }

impl Controller for AtheneController { const BASE_PATH: &'static str = "/api/v1/athene";

fn method(&self) -> Vec<ControllerMethod<Self>>
where
    Self: Sized,
{
    let r = ControllerBuilder::new();
    r.get("/index", |_, _req: Request| async {
        "athene is a simple and lightweight rust web framework based on Hyper"
    })
    .post("/add", Self::add)
    .delete("/{label}/{keyword}", Self::delete)
    .put("/update", Self::update)
    .get("/get", Self::get)
    .build()
}

}

impl AtheneController { pub async fn add(&self, mut req: Request) -> impl Responder { let obj = req.parse::().await?; Ok::<_, Error>((200, Json(obj))) }

pub async fn delete(&self, mut req: Request) -> impl Responder {
    let lable = req.param("label");
    let keyword = req.param("keyword");
    /*
       ...
    */
    info!("{}, .... {}", lable, keyword);
    (200, "delete success")
}

async fn update(&self, mut req: Request) -> impl Responder {
    if let Ok(obj) = req.body_mut().parse::<Form<Self>>().await {
        /*
           ...
        */
        info!("{:#?}", obj);
        (200, String::from("update success"))
    } else {
        (400, String::from("update failed"))
    }
}

async fn get(&self, req: Request) -> impl Responder {
    #[derive(Deserialize, Serialize)]
    struct QueryParam<'a> {
        label: &'a str,
        keyword: &'a str,
    }

    let arg = req.query::<QueryParam>()?;
    /*
       ...
    */
    let res = json!(&arg);
    Ok::<_, Error>(res)
}

}

// ================================ Macro Controller =================================== pub struct UserController {}

// http://127.0.0.1:7878/api/v1/user

[controller(prefix = "api", version = 1, name = "user")]

impl UserController { #[get("//*")] pub async fn matchanyroute(&self, req: Request) -> impl Responder { let uripath = req.uri().path().tostring(); let method = req.method().tostring(); (200, format!("uri : {}, method: {}", uripath, method)) }

#[delete("/{username}/{age}")]
pub async fn delete_by_param(&self, username: String, age: Option<u16>) -> impl Responder {
    (
        200,
        format!("username is : {}, and age is : {:?}", username, age),
    )
}

#[get("/get_query_1")]
pub async fn get_query_1(&self, username: String, age: u16) -> impl Responder {
    (
        200,
        Json(User {
            username,
            age,
            role: None,
        }),
    )
}

#[get("/get_query_2")]
pub async fn get_query_2(&self, request: Request) -> impl Responder {
    let user = request.query::<User>()?;
    Ok::<_, Error>((200, Json(user)))
}

// Context-Type : application/json
#[post("/post_json")]
#[validator] // The user parameter will be validated
pub async fn post_json(&self, user: Json<User>) -> impl Responder {
    Ok::<_,Error>((200, user))
}

// Context-Type : application/x-www-form-urlencoded
#[get("/user_form")]
#[post("/user_form")]
#[validator(exclude("user"))] // The user parameter will not be validated
async fn user_form(&self, user: Form<User>) -> impl Responder {
    (200, user)
}

// Context-Type : application/json  Or application/x-www-form-urlencoded
// Context-Type : application/msgpack Or application/cbor
#[post("/parse_body")]
#[validator] // User will be validated
async fn parse_body(&self, mut req: Request) ->impl Responder{
    let user = req.parse::<User>().await?;
    Ok::<_,Error>((
        200,
        format!("username = {} and age = {}", user.username, user.age),
    ))
}

// Context-Type : application/multipart-formdata
#[post("/files")]
async fn files(&self, mut req: Request) ->impl Responder {
    let files = req.files("files").await?;
    let fist_file_name = files[0].name().unwrap();
    let second_file_name = files[1].name().unwrap();
    let third_file_name = files[2].name().unwrap();
    Ok::<_,Error>((
        200,
        format!(
            "fist {}, second {}, third {}",
            fist_file_name, second_file_name, third_file_name
        ),
    ))
}

}

// ================================ Middleware ====================== struct ApiKeyMiddleware { api_key: String, }

[middleware]

impl ApiKeyMiddleware { async fn next(&self, ctx: Context, chain: &dyn Next) -> Result { if let Some(bearer) = ctx .state .requestunchecked() .header::>() { let token = bearer.0.token(); if token == self.apikey { info!( "Handler {} will be used", ctx.metadata.name.unwrap_or("unknown") ); chain.next(ctx).await } else { info!("Invalid token"); Ok(ctx) } } else { info!("Not Authenticated, "); Ok(ctx) } } }

async fn logmiddleware(ctx: Context, next: &'static dyn Next) -> Result { info!( "new request on path: {}", ctx.state.requestunchecked().uri().path() );

let ctx = next.next(ctx).await?;

info!(
    "new response with status: {}",
    ctx.state.response_unchecked().status()
);
Ok(ctx)

}

// ================================ Handler ===================================

pub async fn hello(_req: Request) -> impl Responder { (200, "Hello, World!") }

pub async fn user_params(mut req: Request) -> impl Responder { let username = req.param("username"); let age = req.param("age"); let age = age.parse::().unwrap(); ( 200, Json(User { username, age, role: None, }), ) }

pub async fn login(mut req: Request) -> impl Responder { if let Ok(user) = req.body_mut().parse::>().await { ( 200, format!("username: {} , age: {} ", user.username, user.age), ) } else { (400, String::from("Bad user format")) } }

pub async fn signup(mut req: Request) -> impl Responder { let user = req.bodymut().parse::>().await?; Ok::<_,Error>((200, Json(user))) }

pub async fn readbody(mut req: Request) -> impl Responder { let body = req.bodymut().parse::().await?; Ok::<_,Error>((200, body)) }

// ================================ Router =================================== pub fn userrouter(r: Router) -> Router { r.get("/hello", hello) .get("/{username}/{age}", userparams) .post("/login", login) .post("/signup", signup) .put("/readbody", readbody) }

[tokio::main]

pub async fn main() -> Result<(), Error> { tracing_subscriber::fmt().compact().init();

let app = athene::new()
    .router(|r| {

        // ====================== Closure ==========================
        r.get("/", |_req: Request| async {
            "Welcome to the athene web framework "
        })


        // Permanent Redirect
        .get("/permanent", |_req: Request| async {
            let res = Builder::new();
            res.redirect(StatusCode::PERMANENT_REDIRECT, "/")
        })


        // Temporary Redirect
        .get("/temporary", |req: Request| async move {
            let uri = req.uri().path();
            info!("uri = {}", uri);
            let res = Builder::new();
            res.redirect(StatusCode::TEMPORARY_REDIRECT, "/download")
        })


        .get("/upload", |_req: Request| async {
            html!(INDEX_HTML)
        })


        // Context-Type : application/multipart-formdata
        .post("/upload", |mut req: Request| async move {
            req.upload("file", "temp").await
        })


        // Content-Disposition: application/octet-stream
        .get("/download", |_req: Request| async {
            let mut res = status!(hyper::StatusCode::OK);
            res.write_file("templates/author.txt", DispositionType::Attachment)?;
            Ok::<_, Error>(res)
        })


        .delete("/delete", |req: Request| async move {
            let path = req.uri().path().to_string();
            (200, path)
        })


        // use json!()
        .patch("/patch", |_req: Request| async {
            let user = User {
                username: "Rustacean".to_string(),
                age: 18,
                role: None,
            };
            json!(&user)
        })


        // Template rendering
        .get("/html", |_req: Request| async {
            #[derive(Serialize, Template)]
            #[template(path = "favorite.html")]
            pub struct Favorite<'a> {
                title: &'a str,
                time: i32,
            }

            let love = Favorite {
                title: "rust_lang",
                time: 1314,
            };
            html!(love.render().unwrap())
        })
    })

    .router(user_router)

    .router(|r| {
        r.controller(UserController {})
            .controller(AtheneController::default())
    })

    .middleware(|m| {
        m.apply(
            ApiKeyMiddleware {
                api_key: "athene".to_string(),
            },
            vec!["/api/v1/user"],
            vec!["/api/v1/athene"],
        )
        .apply(log_middleware, vec!["/"], None)
    })
    .build();

app.listen("127.0.0.1:7878").await

}

static INDEX_HTML: &str = r#" Upload Test

Upload Test

"#; ````