athene_macro-0.1.0 has been yanked.
Useage
use askama::Template;
use athene::prelude::*;
use athene::{html, json, status};
use serde::{Deserialize, Serialize};
use tracing::info;
// use validator::{Validate, ValidationError};
// #[derive(Serialize, Deserialize, Clone, Validate)]
#[derive(Serialize, Deserialize, Clone)]
pub struct User {
// #[validate(email)]
pub username: String,
// #[validate(range(min = 18, max = 20))]
pub age: u16,
}
// ================================ Controller ===================================
pub struct UserController {}
// http://127.0.01:7878/api/v1/user
#[controller(prefix = "api", version = 1, name = "user")]
impl UserController {
#[delete("/<username>/<age>")]
pub async fn delete_by_param(&self, username: String, age: Option<u16>) -> (u16, String) {
(
200,
format!("username is : {}, and age is : {:?}", username, age),
)
}
#[get("/get_query_1")]
pub async fn get_query_1(&self, username: String, age: u16) -> (u16, Json<User>) {
(200, Json(User { username, age }))
}
#[get("/get_query_2")]
pub async fn get_query_2(&self, request: Request) -> Result<(u16, Json<User>)> {
let user = request.query::<User>()?;
Ok((200, Json(user)))
}
// Context-Type : application/json
#[post("/post_json")]
// #[validator(exclude("user"))]
pub async fn post_json(&self, user: Json<User>) -> impl Responder {
(200, user)
}
// Context-Type : application/x-www-form-urlencoded
#[get("/user_form")]
#[post("/user_form")]
// #[validator(exclude("user"))]
async fn user_form(&self, user: Form<User>) -> (u16, Form<User>) {
(200, user)
}
// Context-Type : application/json Or application/x-www-form-urlencoded
// Context-Type : application/msgpack Or application/cbor
#[post("/parse_body")]
async fn parse_body(&self, mut req: Request) -> Result<(u16, String)> {
let user = req.parse::<User>().await?;
Ok((
200,
format!("username = {} and age = {}", user.username, user.age),
))
}
// Context-Type : application/multipart-formdata
#[post("/files")]
async fn files(&self, mut req: Request) -> Result<(u16, String)> {
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((
200,
format!("fist {}, second {}, third {}", fist_file_name, second_file_name,third_file_name),
))
}
// Context-Type : application/multipart-formdata
#[post("/upload")]
async fn upload(&self, mut req: Request) -> impl Responder {
if let Ok(res) = req.upload("file", "temp").await {
(200, res)
} else {
(200, String::from("Bad Request"))
}
}
}
// ================================ Middleware ======================
struct ApiKeyMiddleware(String);
#[middleware]
impl ApiKeyMiddleware {
pub fn new(api_key: &str) -> Self {
ApiKeyMiddleware(api_key.to_string())
}
async fn next(&self, ctx: Context, chain: &dyn Next) -> Result<Context, Error> {
if let Some(Ok("Bearer secure-key")) = ctx
.state
.request_unchecked()
.headers()
.get("authorization")
.map(|auth_value| auth_value.to_str())
{
info!("Authenticated");
} else {
info!("Not Authenticated");
}
info!(
"Handler {} will be used",
ctx.metadata.name.unwrap_or("unknown")
);
chain.next(ctx).await
}
}
async fn log_middleware(ctx: Context, next: &'static dyn Next) -> Result<Context, Error> {
info!(
"new request on path: {}",
ctx.state.request_unchecked().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) -> (u16, &'static str) {
(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::<u16>().unwrap();
(200, Json(User { username, age }))
}
pub async fn login(mut req: Request) -> impl Responder {
if let Ok(user) = req.body_mut().parse::<Json<User>>().await {
(
200,
format!("username: {} , age: {} ", user.username, user.age),
)
} else {
(400, String::from("Bad user format"))
}
}
pub async fn sign_up(mut req: Request) -> Result<(u16, Json<User>)> {
let user = req.body_mut().parse::<Form<User>>().await?;
Ok((200, Json(user)))
}
pub async fn read_body(mut req: Request) -> Result<(u16, String)> {
let body = req.body_mut().parse::<String>().await?;
Ok((200, body))
}
// ================================ Router ===================================
pub fn user_router(r: Router) -> Router {
r.get("/hello", hello)
.get("/{username}/{age}", user_params)
.post("/login", login)
.post("/sign_up", sign_up)
.put("/read_body", read_body)
}
#[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 "
})
// 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,
};
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 {}))
.middleware(|m| {
m.apply(ApiKeyMiddleware::new("secure-key"), vec!["/"], vec!["/api"])
.apply(log_middleware, vec!["/api"], None)
})
.build();
app.listen("127.0.0.1:7878").await
}