[examples](https://gitee.com/unsafe-rust/examples.git) [hypers_rbatis_admin](https://gitee.com/unsafe-rust/hypers_rbatis_admin.git) ## ⚡️ Quick Start ### Cargo.toml ````toml [dependencies] hypers = { version = "0.14", features = ["full","openapi","debug"] } tokio = { version = "1", features = ["full"] } serde = { version = "1", features = ["derive"] } ```` ### Rust Code ````rust use hypers::{hyper::StatusCode, prelude::*, tracing::info}; use serde::{Deserialize, Serialize}; use std::sync::LazyLock; use tokio::sync::Mutex; static STORE: LazyLock = LazyLock::new(new_store); pub type Db = Mutex>; pub fn new_store() -> Db { Mutex::new(Vec::new()) } #[derive(Serialize, Deserialize, Clone, Debug, ToSchema)] pub struct Todo { #[hypers(schema(example = 1))] pub id: u64, #[hypers(schema(example = "Buy coffee"))] pub text: String, pub completed: bool, } struct Api; #[openapi(name = "/api", tag = "api1 todos")] impl Api { /// List todos. #[get( "/list_todos", parameter( ("offset", description = "Offset is an query paramter."), ("limit", description = "Offset is an query paramter."), ) )] async fn list_todos(offset: Query, limit: Query) -> Json> { let todos = STORE.lock().await; let todos: Vec = todos .clone() .into_iter() .skip(offset.0) .take(limit.0) .collect(); Json(todos) } /// Create new todo. #[post("/create_todo", status(201, 409))] async fn create_todo(req: Json) -> Result { let mut vec = STORE.lock().await; for todo in vec.iter() { if todo.id == req.id { return Err(StatusError::bad_request().detail("todo already exists")); } } vec.push(req.0); Ok(StatusCode::CREATED) } } struct Base; #[openapi(tag = "api2 todos")] impl Base { /// Update existing todo. #[patch("/update_todo/{id}", status(200, 404))] async fn update_todo(id: Path, updated: Json) -> Result { let mut vec = STORE.lock().await; for todo in vec.iter_mut() { if todo.id == *id { *todo = (*updated).clone(); return Ok(StatusCode::OK); } } Err(StatusError::not_found()) } #[delete("/{id}", status(200, 401, 404))] async fn delete_todo(id: Path) -> Result { let mut vec = STORE.lock().await; let len = vec.len(); vec.retain(|todo| todo.id != *id); let deleted = vec.len() != len; if deleted { Ok(StatusCode::NO_CONTENT) } else { Err(StatusError::not_found()) } } #[post("/upload")] async fn upload(file: FilePart) -> Response { let mut res = Response::default(); let dest = format!("temp/{}", file.name.clone().unwrap_or("file".to_owned())); println!("{dest}"); let info = if let Err(e) = std::fs::copy(file.path.clone(), std::path::Path::new(&dest)) { res.status(StatusCode::INTERNAL_SERVER_ERROR); format!("file not found in request: {e}") } else { format!("File uploaded to {dest}") }; res.render(Text::Plain(info)) } #[post("/uploads")] async fn uploads(files: FileParts) -> Response { let mut msgs = Vec::with_capacity(files.len()); let mut res = Response::default(); for file in files.0 { let dest = format!("temp/{}", file.name.clone().unwrap_or("file".to_owned())); if let Err(e) = std::fs::copy(file.path.clone(), std::path::Path::new(&dest)) { res.status(StatusCode::INTERNAL_SERVER_ERROR) .body(format!("file not found in request: {e}")); return res; } else { msgs.push(dest); } } res.body(format!("Files uploaded:\n\n{}", msgs.join("\n"))); return res; } } pub async fn upload(_: Request) -> impl Responder { Text::Html( r#" Upload file

Upload file

"#, ) } pub async fn uploads(_: Request) -> Response { let res = Response::default(); res.render(Text::Html( r#" Upload files

Upload files

"#, )) } pub async fn index(_: Request) -> impl Responder { Text::Html( r#" Oapi todos "#, ) } const USER_NAME: &str = "admin"; const PASS_WORD: &str = "123456"; const LOGIN_HTML: &str = r#" swagger-ui login

swagger-ui

"#; #[hook] pub async fn auth_token(req: Request, next: Next<'_>) -> impl Responder { if let Some(session) = req.session() { let username = session.get::("username"); let password = session.get::("password"); println!("username = {:?}", username); println!("password = {:?}", password); return next.next(req).await; } Response::default().render(Text::Html(LOGIN_HTML)) } pub async fn swagger_login(mut req: Request) -> impl Responder { let username = req.form::("username").await; let password = req.form::("password").await; let mut res = Response::default(); if let (Ok(name), Ok(pass)) = (username, password) { if name.eq(USER_NAME) && pass.eq(PASS_WORD) { let mut session = Session::new(); let _ = session.insert("username", name); let _ = session.insert("password", pass); res.set_session(session); res.redirect(StatusCode::SEE_OTHER, "/swagger_ui/"); return res; } } res.render(Text::Html(LOGIN_HTML)) } #[tokio::main] async fn main() -> Result<()> { tracing_subscriber::fmt().init(); std::fs::create_dir_all("temp").unwrap(); let openapi = OpenApi::new("todos api", "0.0.1"); let mut root = OpenApiService::new(openapi) .push(Api) .push(Base) .openapi("/api-doc/openapi.json"); let session_hook = SessionHook::new( CookieStore::new(), b"secretabsecretabsecretabsecretabsecretabsecretabsecretabsecretab", )?; root.hook(session_hook, None, None); root.hook(auth_token, vec!["/swagger_ui/"], vec!["/swaggerLogin"]); root.get("/", index); // http://127.0.0.1:7878/ root.get("/upload", upload); root.get("/uploads", uploads); root.post("/swaggerLogin", swagger_login); let swagger = SwaggerUi::new("/api-doc/openapi.json"); root.get("/swagger_ui/*", swagger); // http://127.0.0.1:7878/swagger_ui/ let rapidoc = RapiDoc::new("/api-doc/openapi.json"); root.get("/rapidoc", rapidoc); // http://127.0.0.1:7878/rapidoc let redoc = ReDoc::new("/api-doc/openapi.json"); root.get("/redoc", redoc); // http://127.0.0.1:7878/redoc let scalar: Scalar = Scalar::new("/api-doc/openapi.json"); root.get("/scalar", scalar); // http://127.0.0.1:7878/scalar info!("router = {:#?}", root); let listener = hypers::TcpListener::bind("127.0.0.1:7878").await?; hypers::listen(root, listener).await } `````