ohkami - [狼] means wolf in Japanese - is simple and macro free web framework for Rust.
Reorganized middleware system and added after-handling middleware:
```rust fn main() -> Result<()> { let middleware = Middleware::new() .beforeGET("/", async |c| { tracing::info!("Helllo, middleware!"); c }) .afterANY("/api/*", async |res| { res.add_header( Header::AccessControlAllowOrigin, "mydomain:8000" ); res });
// ...
} ```
Context
and returns Context
Response
and returns Response
*
). In current ohkami, wildcard doesn't match empty string (for example, /api/*
matches /api/users
and doesn't match /api
). This design may change in future version.Improved json!
macro:
rust
json!(100)
rust
json!("Hello, world!")
rust
json!({"ok": true})
rust
let id = 324;
Response::OK(json!({"id": id}))
toml
[dependencies]
ohkami = "0.8.1"
```rust use ohkami::prelude::*;
fn main() -> Result<()> { Ohkami::default() .GET("/", || async { Response::OK("Hello, world!") }) .howl(":3000") } ```
```rust
async fn ( Context?, {path param 1}?, {path param 2}?, {impl JSON}? ) -> Result
// ?
means "this is optional".
``
- path param:
String | usize | u64 | usize | i64 | i32`
- Current ohkami doesn't handle more than 2 path parameters. This design may change in future version.
```rust // c: Context
let name: &str = c.req.query("name")?;
let count: usize = c.req.query("count")?; ```
```rust use std::{thread::sleep, time::Duration};
fn main() -> Result<()> { Ohkami::default() .GET("/sleepy/:time/:name", sleepy_hello) .howl("localhost:8080") }
async fn sleepyhello(time: u64, name: String) -> Result
sleep(Duration::from_secs(time));
Response::OK(format!("Hello {name}, I'm so sleepy..."))
} ```
Add serde = { version = "1.0", features = ["derive"] }
in your dependencies ( JSON
requires it internally )
```rust
struct User { id: i64, name: String, }
async fn reflect(user: User) -> Result
async fn reflect_name(user: User) -> Result
```rust use ohkami::{ prelude::*, group::{GET, POST} // import this };
struct User { id: usize, name: String, }
fn main() -> Result<()> { Ohkami::default() .GET("/", || async { Response::OK("Hello!") }) .route("/api", GET(hello_api).POST(reflect) ) .howl(":3000") }
async fn hello_api() -> Result
async fn reflect(payload: User) -> Result
rust
let host = c.req.header(Header::Host)?;
rust
async fn reflect_xcustom_header_value(c: Context) -> Result<Response> {
let custom_header_value = c.req.header("X-Custom")?;
c.OK(format!("`X-Custom`'s value is {custom_header_value}"))
}
```rust c.addheader(Header::AccessControlAllowOrigin, "mydomain:8000"); // or c.addheader("Access-Control-Allow-Origin", "mydomain:8000");
// Response
also has the same method
rust
use ohkami::prelude::*;
use ohkami::Header::AccessControlAllowOrigin;
async fn cors(mut res: Response) -> Response { res.add_header(AccessControlAllowOrigin, "mydomain:8000"); res }
fn main() -> Result<()> { let my_middleware = Middleware::new() .afterANY("/api/*", cors);
// ...
```
text/plain
rust
Response::OK("Hello, world!")
rust
c.OK("Hello, world!")
application/json
```rust Response::OK(json!({"ok": true}))
c.OK(json!(100))
c.OK(json!("Hello, world!"))
rust
async fn reflect_id(id: u64) -> Result
OK
can take JSON
-derived value directly:
```rust
struct User { id: u64, name: String, }
// ...
let user = User { id: 1, name: String::from("John") };
Response::OK(user) // or c.OK(user) ```
```rust makeohkamiresult()?;
// or, you can add an error context message: makeohkamiresult() .else(|e| e.errorcontext("failed to get user data"))?;
// or discard original error:
makeohkamiresult()
.else(|| Response::InternalServerError("can't get user"))?;
// or
.else(|| Response::InternalServerError(None))?;
rust
makesomeresult(/* can't use ?
*/)
.else(|e| Response::InternalServerError(e.tostring()))?;
makesomeresult() .else(|| Response::InternalServerError(None))?; ```
rust
let handler = self.handler
._else(|| Response::NotFound("handler not found"))?;
// or
._else(|| Response::NotFound(None))?;
rust
(count < 10)
._else(|| Response::BadRequest("`count` must be less than 10"))?;
// or
._else(|| Response::BadRequest(None))?;
Add tracing
and tracing_subscriber
in your dependencies
.
```rust
fn main() -> Result<()> {
let config = Config {
logsubscribe: Some(
tracingsubscriber::fmt()
.withmaxlevel(tracing::Level::TRACE)
/* default value:
tracing_subscriber::fmt()
.with_mac_level(tracing::Level::DEBUG)
*/
),
..Default::default()
};
Ohkami::with(config)
.GET("/", || async {Response::OK("Hello!")})
} ```
Eneble one of following pairs of features:
- sqlx
and postgres
- sqlx
and mysql
rust
let config = Config {
db_profile: DBprofile {
options: PgPoolOptions::new().max_connections(20),
url: DB_URL.as_str(),
},
..Default::default()
};
Eneble one of following pairs of features:
- sqlx
and postgres
- sqlx
and mysql
rust
let user = sqlx::query_as::<_, User>(
"SELECT id, name FROM users WHERE id = $1"
).bind(1)
.fetch_one(c.pool())
.await?; // `Response` implements `From<sqlx::Error>`
```rust fn main() -> Result<()> { let middleware = Middleware::new() .beforeANY("*", |c| async { tracing::info!("Hello, middleware!"); c });
Ohkami::with(middleware)
.GET("/", || async {
Response::OK("Hello!")
})
.howl("localhost:3000")
}
rust
fn main() -> Result<()> {
let config = Config {
logsubscribe: Some(
tracingsubscriber::fmt()
.withmaxlevel(tracing::Level::TRACE)
),
..Default::default()
};
let middleware = Middleware::new()
.beforeANY("*", |c| async {
tracing::info!("Hello, middleware!");
c
});
let thirdparty_middleware = some_external_crate::x;
Ohkami::with(config.and(middleware).and(x))
.GET("/", || async {
Response::OK("Hello!")
})
.howl("localhost:3000")
} ```
main
function:
```rust
fn server() -> Ohkami {
Ohkami::default()
.GET("/", || async {
Response::OK("Hello!")
})
}fn main() -> Result<()> {
server().howl(":3000")
}
2. import `testing::Test` and other utils
rust
mod test { use ohkami::{Ohkami, response::Response, testing::{Test, Request, Method}}; use once_cell::sync::Lazy;
static SERVER: Lazy<Ohkami> = Lazy::new(|| super::server());
#[test]
fn test_hello() {
let req = Request::new(Method::GET, "/");
SERVER.assert_to_res(&req, Response::OK("Hello!"));
}
} ```
ohkami is not for producntion use now. Please give me your feedback ! → GetHub issue
This project is licensed under MIT LICENSE (LICENSE-MIT or https://opensource.org/licenses/MIT).