23个版本
0.9.0-alpha.2 | 2020年1月7日 |
---|---|
0.8.0 | 2019年12月21日 |
0.7.18 | 2019年11月9日 |
0.7.8 | 2019年6月20日 |
#1166 in HTTP服务器
45 个月下载量
用于 2 存储库
64KB
1.5K SLoC
Thruster

快速直观的Rust Web框架
没有时间阅读文档?看看
✅ 稳定运行 ✅ 运行速度快 ✅ 不使用不安全代码
功能
- 基于async/await构建
- 与Hyper兼容
- SSL就绪
- 可测试
- 静态文件服务
- socketio
- gRPC,以及更多实验性的非tonic的gRPC
- 依赖注入
动机
Thruster是一个Web框架,旨在使开发者在项目和团队之间保持高效和一致性。其目标是
- 性能
- 简单
- 直观
Thruster还
- 不使用
不安全
- 在稳定Rust中工作
快速
Thruster可以与不同的服务器后端一起运行,并在它们之上提供了一个很好的包装层。这意味着它可以跟上Hyper、Actix或甚至ThrusterServer(一个自制的http引擎)等类似框架的最新和最好的变化。
直观
基于像Koa和Express这样的框架,Thruster旨在成为开发者的乐趣。
示例
要运行示例,请输入以下命令:cargo run --example <example-name>
。例如,cargo run --example hello_world
并打开https://127.0.0.1:4321/
基于中间件的
使新的async await代码能够工作的核心部分是使用#[middleware_fn]
属性来指定中间件函数(这将标记中间件,使其与Thruster构建的稳定futures版本兼容),然后在实际路由中使用m!
宏。
使用async await的一个简单示例
use std::boxed::Box;
use std::future::Future;
use std::pin::Pin;
use std::time::Instant;
use thruster::{App, BasicContext as Ctx, Request};
use thruster::{m, middleware_fn, MiddlewareNext, MiddlewareResult, Server, ThrusterServer};
#[middleware_fn]
async fn profile(context: Ctx, next: MiddlewareNext<Ctx>) -> MiddlewareResult<Ctx> {
let start_time = Instant::now();
context = next(context).await;
let elapsed_time = start_time.elapsed();
println!(
"[{}μs] {} -- {}",
elapsed_time.as_micros(),
context.request.method(),
context.request.path()
);
Ok(context)
}
#[middleware_fn]
async fn plaintext(mut context: Ctx, _next: MiddlewareNext<Ctx>) -> MiddlewareResult<Ctx> {
let val = "Hello, World!";
context.body(val);
Ok(context)
}
#[middleware_fn]
async fn four_oh_four(mut context: Ctx, _next: MiddlewareNext<Ctx>) -> MiddlewareResult<Ctx> {
context.status(404);
context.body("Whoops! That route doesn't exist!");
Ok(context)
}
#[tokio::main]
fn main() {
println!("Starting server...");
let mut app = App::<Request, Ctx, ()>::new_basic();
app.get("/plaintext", m![profile, plaintext]);
app.set404(m![four_oh_four]);
let server = Server::new(app);
server.build("0.0.0.0", 4321).await;
}
错误处理
这里有一个很好的例子
use thruster::errors::ThrusterError as Error;
use thruster::proc::{m, middleware_fn};
use thruster::{map_try, App, BasicContext as Ctx, Request};
use thruster::{MiddlewareNext, MiddlewareResult, MiddlewareReturnValue, Server, ThrusterServer};
#[middleware_fn]
async fn plaintext(mut context: Ctx, _next: MiddlewareNext<Ctx>) -> MiddlewareResult<Ctx> {
let val = "Hello, World!";
context.body(val);
Ok(context)
}
#[middleware_fn]
async fn error(mut context: Ctx, _next: MiddlewareNext<Ctx>) -> MiddlewareResult<Ctx> {
let res = "Hello, world".parse::<u32>()
.map_err(|_| {
let mut context = Ctx::default();
context.status(400);
ThrusterError {
context,
message: "Custom error message".to_string(),
cause: None,
}
}?;
context.body(&format!("{}", non_existent_param));
Ok(context)
}
#[tokio::main]
fn main() {
println!("Starting server...");
let app = App::<Request, Ctx, ()>::new_basic()
.get("/plaintext", m![plaintext])
.get("/error", m![error]);
let server = Server::new(app);
server.build("0.0.0.0", 4321).await;
}
测试
Thruster提供了一套简单的测试套件来测试您的端点,只需如下包括testing
模块
let mut app = App::<Request, Ctx, ()>::new_basic();
...
app.get("/plaintext", m![plaintext]);
...
let result = testing::get(app, "/plaintext");
assert!(result.body == "Hello, World!");
创建自己的中间件模块
中间件制作非常简单!只需创建一个函数并在模块级别导出它。下面,您将看到一段允许对请求进行性能分析的中间件。
#[middleware_fn]
async fn profiling<C: 'static + Context + Send>(
mut context: C,
next: MiddlewareNext<C>,
) -> MiddlewareResult<C> {
let start_time = Instant::now();
context = next(context).await?;
let elapsed_time = start_time.elapsed();
info!("[{}μs] {}", elapsed_time.as_micros(), context.route());
Ok(context)
}
您可能会发现需要允许在上下文中存储更具体的数据,例如,也许您希望能够将查询参数填充到散列表中供其他中间件稍后使用。为了做到这一点,您可以创建一个额外的trait,中间件下游必须遵守此trait。查看提供的query_params中间件示例。
其他或自定义后端
Thruster能够在某种服务器之上提供路由层,例如,在上面的Hyper片段中。只要服务器实现了ThrusterServer
,这就可以应用于任何后端。
use async_trait::async_trait;
#[async_trait]
pub trait ThrusterServer {
type Context: Context + Send;
type Response: Send;
type Request: RequestWithParams + Send;
fn new(App<Self::Request, Self::Context>) -> Self;
async fn build(self, host: &str, port: u16);
}
需要以下内容
- 一种创建服务器的方法。
- 一个函数,将服务器构建成一个可以加载到异步运行时的future。
在build
函数中,服务器实现应该
- 启动某种类型的连接监听器
- 调用
let matched = app.resolve_from_method_and_path(<某些方法>, <某些路径>);
(这是提供实际路由。) - 调用
app.resolve(<传入请求>, matched)
(这是运行链式中间件。)
为什么你应该使用Thruster
- 可以随意更改后端。开箱即用,Thruster现在可以在:actix-web、hyper或自定义后端上使用。
- Thruster支持从框架级别进行测试。
- @trezm当没有人提交PR或打开问题时会感到孤独。
- Thruster对于中间件概念(如路由守卫)来说更加简洁。以下是在actix中限制IP的示例
fn ip_guard(head: &RequestHead) -> bool {
// Check for the cloudflare IP header
let ip = if let Some(val) = head.headers().get(CF_IP_HEADER) {
val.to_str().unwrap_or("").to_owned()
} else if let Some(val) = head.peer_addr {
val.to_string()
} else {
return false;
};
"1.2.3.4".contains(&ip)
}
#[actix_web::post("/ping")]
async fn ping() -> Result<HttpResponse, UserPersonalError> {
Ok(HttpResponse::Ok().body("pong"))
}
...
web::scope("/*")
// This is confusing, but we catch all routes that _aren't_
// ip guarded and return an error.
.guard(guard::Not(ip_guard))
.route("/*", web::to(HttpResponse::Forbidden)),
)
.service(ping);
...
这里是Thruster
#[middleware_fn]
async fn ip_guard(mut context: Ctx, next: MiddlewareNext<Ctx>) -> MiddlewareResult<Ctx> {
if "1.2.3.4".contains(&context.headers().get("Auth-Token").unwrap_or("")) {
context = next(context).await?;
Ok(context)
} else {
Err(Error::unauthorized_error(context))
}
}
#[middleware_fn]
async fn ping(mut context: Ctx, _next: MiddlewareNext<Ctx>) -> MiddlewareResult<Ctx> {
context.body("pong");
Ok(context)
}
...
app.get("/ping", m![ip_guard, plaintext]);
...
更直接一点是件好事!
为什么你不应该使用Thruster
- 维护者很少(基本上只有一个人。)
- 还有一些其他项目经过了更多的实战考验。Thruster在生产环境中使用,但没有任何地方是你知道的或者重要的。
- 它还没有被那些聪明的家伙优化。@trezm 尽力而为,但总是被他的狗分心。
- 说真的,这个框架
可能是很棒的,但它肯定没有像其他框架那样经过仔细的检查。你的帮助可以大大提高其安全性和鲁棒性,但我们可能还没有达到那个阶段。
如果你看到了这里,感谢你阅读!随时欢迎联系。
依赖项
~9–12MB
~198K SLoC