9 个版本 (5 个重大变更)
0.6.2 | 2024 年 7 月 24 日 |
---|---|
0.6.1 | 2024 年 7 月 24 日 |
0.5.0 | 2024 年 5 月 22 日 |
0.4.1 | 2024 年 3 月 31 日 |
0.1.0 | 2023 年 12 月 29 日 |
#1498 in 网页编程
每月 874 次下载
用于 zip_static_handler
1MB
26K SLoC
可组合的 Rust 异步网页框架
特性
- 100% 安全的 Rust
- 可组合的 API,满足高级使用,并可以轻松扩展到和/或与中/低级别混合
- 静态类型,使用运行时动态类型转换最少
- 最小依赖树和快速编译时间
lib.rs
:
一个专注于内存效率、可组合性和快速编译时间的网页框架。
快速开始
use xitca_web::{handler::handler_service, route::get, App};
fn main() -> std::io::Result<()> {
App::new()
.at("/", get(handler_service(|| async { "Hello,World!" })))
.serve()
.bind("localhost:8080")?
.run()
.wait()
}
内存高效
- 零拷贝魔法类型
- 零成本服务树
零拷贝
use xitca_web::{
error::Error,
handler::{handler_service, json::LazyJson, path::PathRef},
route::{get, post},
App
};
// PathRef is able to borrow http request's path string as reference
// without copying it.
async fn url_query(PathRef(path): PathRef<'_>) -> &'static str {
println!("{path}");
"zero copy path"
}
// deserializable user type.
#[derive(serde::Deserialize)]
struct User<'a> {
name: &'a str
}
// LazyJson is able to lazily deserialize User type with zero copy &str.
async fn json(lazy: LazyJson<User<'_>>) -> Result<&'static str, Error> {
let User { name } = lazy.deserialize()?;
println!("{name}");
Ok("zero copy json")
}
// Almost all magic extract types in xitca-web utilize zero copy
// to avoid unnecessary memory copy.
App::new()
// a route handling incoming url query.
.at("/query", get(handler_service(url_query)))
// a route handling incoming json object.
.at("/json", post(handler_service(json)))
.serve()
.bind("localhost:8080")?
.run()
.wait()
零成本
use xitca_web::{
handler::{handler_service},
http::WebResponse,
route::get,
middleware::Extension,
service::{Service, ServiceExt},
App, WebContext
};
App::new()
.at("/", get(handler_service(|| async { "hello,world!" })))
// ServiceExt::enclosed_fn combinator enables async function as middleware.
// the async function is unboxed and potentially inlined with other async services
// for efficient binary code with less memory allocation.
.enclosed_fn(middleware_fn)
// ServiceExt::enclosed combinator enables type impl Service trait as middleware.
// the middleware trait method is unboxed and potentially inlined with other async services
// for efficient binary code with less memory allocation.
.enclosed(Extension::new(()))
.serve()
.bind("localhost:8080")?
.run()
.wait()
// a simple middleware just forward request to inner service logic.
async fn middleware_fn<S, T, E>(service: &S, ctx: WebContext<'_>) -> Result<T, E>
where
S: for<'r> Service<WebContext<'r>, Response = T, Error = E>
{
service.call(ctx).await
}
可组合
- 易于混合各种级别的抽象和较少意见的 API
- 跨 crate 集成的常用类型和特质
抽象多样性
use xitca_web::{
body::ResponseBody,
error::Error,
handler::{handler_service, handler_sync_service, FromRequest},
http::{Method, WebResponse},
route::get,
service::fn_service,
App, WebContext
};
App::new()
// high level abstraction. see fn high for detail.
.at("/high", get(handler_service(high)))
// low level abstraction. see fn low for detail.
.at("/low", get(fn_service(low)))
// abstraction for synchronous. see fn sync for detail.
.at("/sync", get(handler_sync_service(sync)))
.serve()
.bind("localhost:8080")?
.run()
.wait()
// magic function with arbitrary receiver type and output type
// that can be extracted from http requests and packed into http
// response.
async fn high(method: &Method) -> &'static str {
// extract http method from http request.
assert_eq!(method, Method::GET);
// pack string literal into http response.
"high level"
}
// function with concrete typed input and output where http types
// are handled manually.
async fn low(ctx: WebContext<'_>) -> Result<WebResponse, Error> {
// manually check http method.
assert_eq!(ctx.req().method(), Method::GET);
// high level abstraction can be opt-in explicitly if desired.
// below is roughly what async fn high did to receive &Method as
// function argument.
let method = <&Method>::from_request(&ctx).await?;
assert_eq!(method, Method::GET);
// manually pack http response.
Ok(WebResponse::new(ResponseBody::from("low level")))
}
// high level abstraction but for synchronous function. this function
// is powered by background thread pool so it does not block the async
// code.
fn sync(method: Method) -> &'static str {
assert_eq!(method, Method::GET);
// blocking thread for long period of time does not impact xitca-web
// async internal.
std::thread::sleep(std::time::Duration::from_secs(3));
"sync"
}
中间件可组合性
use xitca_web::{
error::Error,
handler::{handler_service},
http::WebResponse,
route::get,
service::{Service, ServiceExt},
App, WebContext
};
// ServiceExt::enclosed_fn combinator enables async function as middleware.
// in xitca_web almost all service can be enclosed by an middleware.
App::new()
.at("/",
get(
// apply middleware to handler_service
handler_service(|| async { "hello,world!" })
.enclosed_fn(middleware_fn)
)
// apply middleware to route
.enclosed_fn(middleware_fn)
)
// apply middleware to application
.enclosed_fn(middleware_fn)
.serve()
.bind("localhost:8080")?
.run()
.wait()
// a simple middleware just forward request to inner service logic.
async fn middleware_fn<S, T, E>(service: &S, ctx: WebContext<'_>) -> Result<T, E>
where
S: for<'r> Service<WebContext<'r>, Response = T, Error = E>
{
service.call(ctx).await
}
有关更详细的中间件文档,请参阅 [中间件]
跨 crate 集成
在 xitca-web 应用程序中使用 tower-http。
use tower_http::services::ServeDir;
use xitca_web::{
service::tower_http_compat::TowerHttpCompat,
App
};
App::new()
.at("/", TowerHttpCompat::new(ServeDir::new("/some_folder")))
.serve()
.bind("localhost:8080")?
.run()
.wait()
快速编译时间
- 增量 proc macro
- 轻量级依赖树
可选的 proc macro
在 xitca-web 中,proc macro 是可选的。这导致快速编译时间,且没有公开的 proc macro。但仍然可以为更高层次的 API 启用宏。
use xitca_web::{codegen::route, App};
#[tokio::main]
async fn main() -> std::io::Result<()> {
App::new()
.at_typed(index)
.serve()
.bind("localhost:8080")?
.run()
.await
}
#[route("/", method = get)]
async fn index() -> &'static str {
"Hello,World!"
}
宏代码生成模块。http 类型路由服务。
示例
// a simple async function service.
let service = handler_service(|_: ()| async { });
let custom_method = Method::from_bytes(b"huh")?;
// route get http method and a user custom method to handler_service
Route::new([Method::GET, custom_method]).route(service.clone());
// shortcut for single http method route(s). they can chained multiple times.
get(service.clone()).post(service.clone()).put(service);
依赖
~3–15MB
~179K SLoC