56个版本 (18个破坏性更新)
0.19.0 | 2024年6月17日 |
---|---|
0.17.1 | 2024年4月23日 |
0.15.0 | 2024年2月20日 |
0.10.0 | 2023年10月28日 |
0.6.3 | 2022年12月30日 |
#45 in HTTP服务器
505KB
11K SLoC
Ohkami
Ohkami - [狼] 日本语中的狼 - 是一个直观和声明性的Web框架。- 无宏和类型安全 的API,用于直观和声明性代码
- 多运行时 支持:
tokio
,async-std
,worker
(Cloudflare Workers)
快速入门
- 添加到
dependencies
# This sample uses `tokio` runtime.
# `async-std` is available by feature "rt_async-std".
[dependencies]
ohkami = { version = "0.19", features = ["rt_tokio"] }
tokio = { version = "1", features = ["full"] }
- 使用Ohkami编写您的第一个代码: examples/quick_start
use ohkami::prelude::*;
use ohkami::typed::status::NoContent;
async fn health_check() -> NoContent {
NoContent
}
async fn hello(name: &str) -> String {
format!("Hello, {name}!")
}
#[tokio::main]
async fn main() {
Ohkami::new((
"/healthz"
.GET(health_check),
"/hello/:name"
.GET(hello),
)).howl("localhost:3000").await
}
- 运行并检查行为
$ cargo run
$ curl https://127.0.0.1:3000/healthz
$ curl https://127.0.0.1:3000/hello/your_name
Hello, your_name!
Cloudflare Workers支持 "rt_worker"
功能
npm create cloudflare ./path/to/project -- --template https://github.com/kana-rus/ohkami-templates/worker
然后您的项目目录包含 wrangler.toml
,package.json
和一个Rust库crate。
使用 npm run dev
进行本地开发,并使用 npm run deploy
部署!
(有关模板的详细信息,请参阅README)
代码片段
处理路径参数
use ohkami::prelude::*;
#[tokio::main]
async fn main() {
Ohkami::new((
"/api/hello/:name"
.GET(hello),
)).howl("localhost:5000").await
}
async fn hello(name: &str) -> String {
format!("Hello, {name}!")
}
处理请求体/查询参数
use ohkami::prelude::*;
use ohkami::typed::status::Created;
use ohkami::typed::{Query, Payload};
use ohkami::builtin::payload::JSON;
/* `serde = 〜` is not needed in your [dependencies] */
use ohkami::serde::{Serialize, Deserialize};
/* Payload + Deserialize for request */
#[Payload(JSON)]
#[derive(Deserialize)]
struct CreateUserRequest<'req> {
name: &'req str,
password: &'req str,
}
/* Payload + Serialize for response */
#[Payload(JSON)]
#[derive(Serialize)]
struct User {
name: String,
}
async fn create_user(body: CreateUserRequest<'_>) -> Created<User> {
Created(User {
name: String::from("ohkami")
})
}
/* Shorthand for Payload + Serialize */
#[Payload(JSON/S)]
struct SearchResult {
title: String,
}
#[Query] /* Params like `?lang=rust&q=framework` */
struct SearchQuery<'q> {
lang: &'q str,
#[query(rename = "q")] /* #[serde]-compatible #[query] attribute */
keyword: &'q str,
}
async fn search(condition: SearchQuery<'_>) -> Vec<SearchResult> {
vec![
SearchResult { title: String::from("ohkami") },
]
}
有效载荷验证
where <validation expression>
在 #[Payload()]
运行验证时或在解析请求时执行。
<validation expression>
是一个具有 self: &Self
返回 Result<(), impl Display>
的表达式。
use ohkami::prelude::*;
use ohkami::{typed::Payload, builtin::payload::JSON};
#[Payload(JSON/D where self.valid())]
struct Hello<'req> {
name: &'req str,
repeat: usize,
}
impl Hello<'_> {
fn valid(&self) -> Result<(), String> {
(self.name.len() > 0).then_some(())
.ok_or_else(|| format!("`name` must not be empty"))?;
(self.repeat > 0).then_some(())
.ok_or_else(|| format!("`repeat` must be positive"))?;
Ok(())
}
}
使用中间件
Ohkami的请求处理系统被称为“fang”,并在该系统上实现了中间件。
use ohkami::prelude::*;
#[derive(Clone)]
struct GreetingFang;
/* utility trait for auto impl `Fang` */
impl FangAction for GreetingFang {
async fn fore<'a>(&'a self, req: &'a mut Request) -> Result<(), Response> {
println!("Welcomm request!: {req:?}");
Ok(())
}
async fn back<'a>(&'a self, res: &'a mut Response) {
println!("Go, response!: {res:?}");
}
}
#[tokio::main]
async fn main() {
Ohkami::with(GreetingFang, (
"/".GET(|| async {"Hello, fangs!"})
)).howl("localhost:3000").await
}
使用具有"sse"
功能的Server-Sent Events。
Ohkami以HTTP/1.1响应,编码为Transfer-Encoding: chunked
。
使用一些反向代理来处理HTTP/2,3。
use ohkami::prelude::*;
use ohkami::typed::DataStream;
use tokio::time::sleep;
async fn sse() -> DataStream<String> {
DataStream::from_iter_async((1..=5).map(|i| async move {
sleep(std::time::Duration::from_secs(1)).await;
Ok(format!("Hi, I'm message #{i} !"))
}))
}
#[tokio::main]
async fn main() {
Ohkami::new((
"/sse".GET(sse),
)).howl("localhost:5050").await
}
Ohkami的打包
use ohkami::prelude::*;
use ohkami::typed::status::{Created, NoContent};
use ohkami::typed::Payload;
use ohkami::builtin::payload::JSON;
#[Payload(JSON/S)]
struct User {
name: String
}
async fn list_users() -> Vec<User> {
vec![
User { name: String::from("actix") },
User { name: String::from("axum") },
User { name: String::from("ohkami") },
]
}
async fn create_user() -> Created<User> {
Created(User {
name: String::from("ohkami web framework")
})
}
async fn health_check() -> NoContent {
NoContent
}
#[tokio::main]
async fn main() {
// ...
let users_ohkami = Ohkami::new((
"/"
.GET(list_users)
.POST(create_user),
));
Ohkami::new((
"/healthz"
.GET(health_check),
"/api/users"
.By(users_ohkami), // nest by `By`
)).howl("localhost:5000").await
}
测试
use ohkami::prelude::*;
use ohkami::testing::*; // <--
fn hello_ohkami() -> Ohkami {
Ohkami::new((
"/hello".GET(|| async {"Hello, world!"}),
))
}
#[cfg(test)]
#[tokio::test]
async fn test_my_ohkami() {
let t = hello_ohkami().test();
let req = TestRequest::GET("/");
let res = t.oneshot(req).await;
assert_eq!(res.status(), Status::NotFound);
let req = TestRequest::GET("/hello");
let res = t.oneshot(req).await;
assert_eq!(res.status(), Status::OK);
assert_eq!(res.text(), Some("Hello, world!"));
}
支持的协议
- HTTP/1.1
- HTTP/2
- HTTP/3
- HTTPS
- Server-Sent Events
- WebSocket
MSRV(最低支持的Rust版本)
最新稳定版
许可证
ohkami遵循MIT许可证(LICENSE或https://opensource.org/licenses/MIT)。
依赖项
约1–15MB
约203K SLoC