7 个不稳定版本 (3 个重大更改)
0.4.2 | 2023年11月11日 |
---|---|
0.4.1 | 2023年7月31日 |
0.4.0 | 2023年6月27日 |
0.3.1 | 2023年6月10日 |
0.1.0 | 2023年3月19日 |
#287 in HTTP 服务器
每月38次下载
在 2 个 Crates 中使用(通过 gcal)
70KB
1.5K SLoC
davisjr:一个简单的 HTTP 框架(用于 rust-lang)
davisjr
在其目标上理想化了 sinatra(Ruby)框架的简洁性,并试图成为其他异步 HTTP 框架(如 tower、warp、axum 和 tide)的替代品。
davisjr
通过实现跨越处理器边界的“责任链”模式,试图实现一个承诺,即“任何处理器也可以是中间件”。简而言之,从第一个处理器返回的内容被传递到第二个处理器,然后返回到第三个处理器,直到所有处理器都处理完毕或接收到错误。错误可以返回有效的状态码或 HTTP 500(内部服务器错误)格式的纯文本错误。
davisjr
最初是 ratpack,一个为 ZeroTier,Inc. 设计的框架。据我所知,它已经没有更新,自 2022 年 4 月以来就没有更新。我正在对其进行硬分叉以改进它。
davisjr 不是什么
- 复杂:
davisjr
并非特别设计用于具有大量路由或与 HTTP 协议复杂交互的服务(例如 SSE 或 Websockets),至少到目前为止。davisjr
非常专注于某些典型的请求/响应周期。 - 详细模式:
davisjr
尽力使自身内部结构和您与之交互都尽可能简单,这是它能做到的最简单的事情。这意味着您需要传递给名为compose_handler!
的宏,该宏用于路由调用,并且您很可能不需要花费时间实现复杂的、极其冗长的特质,甚至不需要对 futures 和async
的工作原理有复杂的理解。 - 专注于一个平台:虽然目前我们只直接支持
tokio
,但没有任何东西阻止我们进入smol
和async-std
的领域。《davisjr》的大多数async
使用都是 futures,这些 futures 最终被tokio
在非常高级的层面上利用。
示例
以下是一个示例,它将全局 应用程序状态 作为身份验证令牌验证中间件处理程序,然后传递给问候处理程序。问候处理程序也可以在不需要身份验证的不同端点重用,这也在示例中演示。
注意:此示例可在 examples/auth-with-state.rs 找到。也可以用 cargo 运行:cargo run --example auth-with-state
。
use davisjr::prelude::*;
// We'll use authstate to (optionally) capture information about the token
// being correct. if it is Some(true), the user was authed, if None, there was no
// authentication performed.
#[derive(Clone)]
struct AuthedState {
authed: Option<bool>,
}
// All transient state structs must have an initial state, which will be
// initialized internally in the router.
impl TransientState for AuthedState {
fn initial() -> Self {
Self { authed: None }
}
}
// our authtoken validator, this queries the app state and the header
// `X-AuthToken` and compares the two. If there are any discrepancies, it
// returns `401 Unauthorized`.
//
// every handler & middleware takes and returns the same params and has the
// same prototype.
//
async fn validate_authtoken(
req: Request<Body>,
resp: Option<Response<Body>>,
_params: Params,
app: App<State, AuthedState>,
mut authstate: AuthedState,
) -> HTTPResult<AuthedState> {
if let (Some(token), Some(state)) = (req.headers().get("X-AuthToken"), app.state().await) {
authstate.authed = Some(state.clone().lock().await.authtoken == token);
Ok((req, resp, authstate))
} else {
Err(Error::StatusCode(
StatusCode::UNAUTHORIZED,
String::default(),
))
}
}
// our `hello` responder; it simply echoes the `name` parameter provided in the
// route.
async fn hello(
req: Request<Body>,
_resp: Option<Response<Body>>,
params: Params,
_app: App<State, AuthedState>,
authstate: AuthedState,
) -> HTTPResult<AuthedState> {
let name = ¶ms["name"];
let bytes = Body::from(format!("hello, {}!\n", name));
if let Some(authed) = authstate.authed {
if authed {
return Ok((
req,
Some(Response::builder().status(200).body(bytes).unwrap()),
authstate,
));
}
} else if authstate.authed.is_none() {
return Ok((
req,
Some(Response::builder().status(200).body(bytes).unwrap()),
authstate,
));
}
Err(Error::StatusCode(
StatusCode::UNAUTHORIZED,
String::default(),
))
}
// our `wildcard` responder, which shows how to use wildcard routes
async fn wildcard(
req: Request<Body>,
_resp: Option<Response<Body>>,
params: Params,
_app: App<State, AuthedState>,
state: AuthedState,
) -> HTTPResult<AuthedState> {
let bytes = Body::from(format!("this route is: {}!\n", params["*"]));
return Ok((
req,
Some(Response::builder().status(200).body(bytes).unwrap()),
state,
));
}
// Our global application state; must be `Clone`.
#[derive(Clone)]
struct State {
authtoken: &'static str,
}
// ServerError is a catch-all for errors returned by serving content through
// davisjr.
#[tokio::main]
async fn main() -> Result<(), ServerError> {
let mut app = App::with_state(State {
authtoken: "867-5309",
});
app.get("/wildcard/*", compose_handler!(wildcard))?;
app.get("/auth/:name", compose_handler!(validate_authtoken, hello))?;
app.get("/:name", compose_handler!(hello))?;
app.serve("127.0.0.1:3000").await?;
Ok(())
}
使用 curl
调用此服务会得到预期的结果
% curl localhost:3000/wildcard/frobnik/from/zorbo
this route is: frobnik/from/zorbo!
% curl localhost:3000/erik
hello, erik!
% curl -D- localhost:3000/auth/erik
HTTP/1.1 401 Unauthorized
content-length: 0
date: Fri, 21 Jan 2022 18:29:03 GMT
% curl -D- -H "X-AuthToken: 867-5309" localhost:3000/auth/erik
HTTP/1.1 200 OK
content-length: 13
date: Fri, 21 Jan 2022 18:29:19 GMT
hello, erik!
更多信息 & 文档
更多详细信息,请参阅 文档。
作者
Erik Hollensbe [email protected]
许可证
BSD 3-Clause
依赖关系
~7–20MB
~283K SLoC