athene-2.0.0 has been yanked.
example
This version v2.x.x is built on hyper v1.0.0-rc.4
use athene's full feature
use athene::prelude::*;
use headers::{authorization::Bearer, Authorization};
use serde::{Deserialize, Serialize};
use tracing::info;
use validator::Validate;
#[derive(Debug, Serialize, Deserialize, Validate, Default)]
pub struct UserController {
#[validate(email)]
pub email: String, // admin@outlook.com
#[validate(range(min = 18, max = 20))]
pub age: u16,
}
// http://127.0.0.1:7878/api/v1/user
#[controller(prefix = "api", version = 1, name = "user")]
impl UserController {
// http://127.0.0.1:7878/api/v1/user/query/?email=admin@outlook.com&age=19
// query params
#[get("/query")] // user will be validate
pub async fn query(&self, user: Query<Self>) -> impl Responder {
(200, Json(user.0))
}
// http://127.0.0.1:7878/api/v1/user/create
// Context-Type : application/json
#[post("/create")] // user will be validate
async fn create(&self, user: Json<Self>) -> impl Responder {
Ok::<_, Error>((200, user))
}
// http://127.0.0.1:7878/api/v1/user/update
// Context-Type : application/x-www-form-urlencoded
#[put("/update")]
#[validator(exclude("user"))] // user will not be validate
async fn update(&self, user: Form<Self>) -> impl Responder {
Ok::<_, Error>((200, user))
}
// http://127.0.0.1:7878/api/v1/user/admin@outlook.com/18
// uri path params
#[delete("/{email}/{age}")]
pub async fn delete(&self, email: String, age: Option<u16>) -> impl Responder {
(
StatusCode::OK,
format!("email is : {}, and age is : {:?}", email, age),
)
}
// http://127.0.0.1:7878/api/v1/user/query_get/?email=admin@outlook.com&age=29
// query params
#[get("/query_get")]
pub async fn query_get(&self, email: String, age: u16) -> impl Responder {
(200, Json(Self { email, age }))
}
// http://127.0.0.1:7878/api/v1/user/get_cookies
#[get("/get_cookies")]
pub async fn get_cookies(&self,cookie_jar: CookieJar) -> impl Responder {
let cookie = cookie_jar.get("Cookie1").unwrap();
println!("cookie = {:?}", cookie);
}
// http://127.0.0.1:7878/api/v1/user/set_cookies
#[get("/set_cookies")]
pub async fn set_cookies(&self) -> impl Responder {
let mut cookie = Cookie::new("Cookie1", "athene");
cookie.set_path("/");
let mut cookie2 = Cookie::new("Cookie2", "athene2");
cookie2.set_path("/");
let mut cookie_jar = CookieJar::new();
cookie_jar.add(cookie);
cookie_jar.add(cookie2);
(200, cookie_jar)
}
}
// Function Middleware
pub async fn log_middleware(ctx: Context, next: &dyn Next) -> Result {
let uri_path = ctx.state.request().map(|req| req.uri().path());
info!("new request on path: {:?}", uri_path);
let continue_ctx = next.next(ctx).await?;
let status = continue_ctx.state.response().map(|res| res.status());
info!("new response with status: {:?}", status);
Ok(continue_ctx)
}
#[derive(Debug, Deserialize, Serialize)]
pub struct MiddlewareError<T> {
pub code: u32,
pub msg: String,
pub data: T,
}
struct ApiKeyMiddleware {
api_key: String,
}
// Macro Middleware
#[middleware]
impl ApiKeyMiddleware {
async fn next(&self, ctx: Context, next: &dyn Next) -> Result {
let req = ctx.state.request()?;
if let Some(bearer) = req.header::<Authorization<Bearer>>() {
let token = bearer.0.token();
if token == self.api_key {
info!(
"Handler {} will be used",
ctx.metadata.name.unwrap_or("unknown")
);
next.next(ctx).await
} else {
info!("Invalid token");
let res = MiddlewareError {
code: 400,
msg: String::from("Invalid token"),
data: "Invalid token",
};
Err(Error::Response(StatusCode::UNAUTHORIZED, json!(res)))
}
} else {
info!("Not Authenticated");
Err(Error::Response(StatusCode::UNAUTHORIZED, json!("Not Authenticated")))
}
}
}
// http://127.0.0.1:7878/upload
pub async fn upload(mut req: Request) -> impl Responder {
let res = req.upload("file", "temp").await?;
if res > 0 {
Ok::<_, Error>((200, "File uploaded successfully"))
} else {
Ok::<_, Error>((400, "File upload failed"))
}
}
fn upload_router<'a>(r: Router) -> Router {
r.get("/upload", |_: Request| async { Html(INDEX_HTML) })
.post("/upload", upload)
}
// Websocket ws://127.0.0.1:7878/hello/ws http://www.jsons.cn/websocket/
pub async fn ws(
mut req: Request,
mut tx: WebSocketSender,
mut rx: WebSocketReceiver,
) -> Result<()> {
let name = req.param::<String>("name")?;
while let Ok(msg) = rx.receive().await {
if let Some(msg) = msg {
match msg {
Message::Text(text) => {
let text = format!("{},{}", name, text);
tx.send(Message::Text(text)).await?;
}
Message::Close(_) => break,
_ => {}
}
}
}
Ok(())
}
#[tokio::main]
pub async fn main() -> Result<()> {
tracing_subscriber::fmt().compact().init();
// Add Router
let app = athene::new().router(upload_router).router(|r| {
r.controller(UserController::default())
.ws("/{name}/ws", ws)
.get("/**", StaticDir::new("todo").with_listing(true))
});
// Add Middleware
let app = app.middleware(|m| {
m.apply(log_middleware, vec!["/"], None).apply(
ApiKeyMiddleware {
api_key: "athene".to_string(),
},
vec!["/api/v1/user"],
vec!["/upload"],
)
});
// Start Server
app.listen("127.0.0.1:7878").await
}
static INDEX_HTML: &str = r#"<!DOCTYPE html>
<html>
<head>
<title>Upload Test</title>
</head>
<body>
<h1>Upload Test</h1>
<form action="/upload" method="post" enctype="multipart/form-data">
<input type="file" name="file" />
<input type="submit" value="upload" />
</form>
</body>
</html>
"#;