Useage
use askama::Template;
use athene::headers::{authorization::Bearer, Authorization};
use athene::prelude::*;
use athene::{html, json, status, text, with};
use serde::{Deserialize, Serialize};
use tera::{Context, Tera};
use tracing::{debug, error, info, warn};
pub async fn askama_template(_: Request) -> impl Responder {
#[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()?)
}
pub async fn tera_template(_: Request) -> impl Responder {
#[derive(Serialize)]
struct Awesome<'a> {
url: &'a str,
name: &'a str,
}
let tera = Tera::new("templates/*")?;
let mut ctx = Context::new();
ctx.insert("title", "Welcome to rust-lang world!");
ctx.insert(
"product",
&vec![
Awesome {
url: "https://github.com/rust-lang",
name: "rust-lang",
},
Awesome {
url: "https://github.com/lapce/lapce",
name: "lapce",
},
],
);
let love = tera.render("index.html", &ctx)?;
with!(love, "text/html; charset=utf-8")
}
struct Logger;
#[async_trait]
impl Middleware for Logger {
async fn apply(&self, req: Request, next: Next<'_>) -> Result {
let method = req.method().to_string();
let uri = req.uri().to_string();
debug!(%method, %uri, "request");
let result = next.next(req).await?;
log_response(method, uri, &result);
Ok(result)
}
}
fn log_response(method: String, uri: String, resp: &Response) {
let status = resp.as_ref().status();
if status.is_server_error() {
error!(%method, %uri, %status, "response");
} else if status.is_client_error() {
warn!(%method, %uri, %status, "response");
} else {
info!(%method, %uri, %status, "response");
}
}
pub struct AuthCheck;
#[async_trait]
impl Middleware for AuthCheck {
async fn apply(&self, req: Request, next: Next<'_>) -> Result {
let auth = req.header::<Authorization<Bearer>>();
match auth {
None => return Ok(Response::status(StatusCode::UNAUTHORIZED)),
Some(bearer) => {
info!("bearer token: {}", bearer.0.token());
next.next(req).await
}
}
}
}
pub fn base_router() -> Router {
let mut app = athene::new();
app.get("/askama_template", askama_template);
app.get("/tera_template", tera_template);
app.get("/param/:name", |req: Request| async move {
let name = req.param("name").unwrap();
name.to_string()
});
app.get("/query", |req: Request| async move {
#[derive(Serialize, Deserialize)]
struct Book {
title: String,
is_publish: bool,
}
json!(req.query::<Book>()?)
});
app.post("/form-urlencoded", |mut req: Request| async move {
#[derive(Serialize, Deserialize)]
struct Rust {
version: String,
edition: String,
}
json!(req.parse::<Rust>().await?)
});
app.post("/json", |mut req: Request| async move {
#[derive(Serialize, Deserialize)]
struct User {
name: String,
age: i32,
}
let user = req.parse::<User>().await?;
json!(user)
});
app.post("/file", |mut req: Request| async move {
let file = req.file("file").await?;
text!(file.name().unwrap().to_string())
});
app.post("/files", |mut req: Request| async move {
let files = req.files("files").await?;
let fist_file_name = files[0].name().unwrap();
let second_file_name = files[1].name().unwrap();
let name = format!("fist {}, second {}", fist_file_name, second_file_name);
text!(name)
});
app.get("/download", |_| async {
let mut res = Response::default();
res.write_file("templates/author.txt", DispositionType::Attachment)?;
Ok(res)
});
app
}
pub async fn websocket_hello(req: Request,mut tx: WebSocketSender,mut rx: WebSocketReceiver) -> Result<()> {
info!("uri = {} , method = {}",req.uri().path(),req.method());
while let Some(msg) = rx.receive().await? {
tx.send(msg).await?;
}
Ok(())
}
pub fn websocket_router() -> Router {
let mut app = athene::new();
app.ws("/ws/index", |_req, mut tx, mut rx| async move {
while let Some(msg) = rx.receive().await? {
tx.send(msg).await?;
}
println!("websocket closed");
Ok(())
});
app.get("/ws/hello", new_ws(websocket_hello));
app
}
pub fn api_router(auth: AuthCheck) -> Router {
let mut api = athene::new();
api.middleware(auth);
api.get("check", |req: Request| async move {
let mut res = status!(StatusCode::OK);
res.text(req.uri().to_string())?;
Ok(res)
});
api.get("user/:name", |req: Request| async move {
let mut res = status!(StatusCode::OK);
let resp = format!("uri = {}\nparams = {:?}\n", req.uri(), req.params());
res.text(resp)?;
Ok(res)
});
api
}
#[tokio::main]
pub async fn main() -> Result<()> {
tracing_subscriber::fmt().compact().init();
let mut app = athene::new();
app.middleware(Logger);
app.get("/", |_| async { "Hello world!\n\n" });
app.group("/base", base_router());
app.static_files("/base/public/*", "public");
app.group("/websocket", websocket_router());
app.group("/api/:version", api_router(AuthCheck));
app.listen("127.0.0.1:8888").await
}
templates/index.html
<title>{{title}}</title>
<ul>
{% for one in product -%}
<li><a href="{{ one.url | safe }}">{{ one.name }}</a></li>
{%- endfor %}
<img src="public/icon.jpg" alt="A rocket icon." height=200 width=200 />
</ul>
templates/favorite.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Rust is one of my favorite programming languages</title>
</head>
<body>
<h4>{{title}}</h4>
<p>{{time}}</p>
</body>
</html>