23次发布
0.6.1 | 2022年1月17日 |
---|---|
0.5.3 | 2021年3月29日 |
0.5.2 | 2020年7月15日 |
0.5.0-rc.5 | 2020年3月31日 |
343在HTTP服务器
66次每月下载
在 6 个Crate中使用
235KB
4.5K SLoC
简介
罗亚是一个受koajs启发的异步Web框架,轻量但强大。
应用
罗亚应用是一个由中间件和端点以堆栈方式组成的结构。
必做的“Hello World”应用
use roa::App;
use roa::preload::*;
use tracing::info;
use std::error::Error as StdError;
#[tokio::main]
async fn main() -> Result<(), Box<dyn StdError>> {
let app = App::new().end("Hello, World");
app.listen("127.0.0.1:8000", |addr| {
info!("Server is listening on {}", addr)
})?
.await?;
Ok(())
}
端点
端点是一个请求处理器。
罗亚内置了一些端点。
-
函数式端点
一个普通的函数式端点是一个具有以下签名的异步函数:
async fn mut Context) -> Result
。use roa::{App, Context, Result}; async fn endpoint(ctx: &mut Context) -> Result { Ok(()) } let app = App::new().end(endpoint);
-
Ok端点
是一个总是返回
Ok(())
的端点let app = roa::App::new().end(());
-
状态端点
Status
是一个总是返回Err(Status)
的端点use roa::{App, status}; use roa::http::StatusCode; let app = App::new().end(status!(StatusCode::BAD_REQUEST));
-
字符串端点
将字符串写入正文。
use roa::App; let app = App::new().end("Hello, world"); // static slice let app = App::new().end("Hello, world".to_owned()); // string
-
重定向端点
重定向到URI。
use roa::App; use roa::http::Uri; let app = App::new().end("/target".parse::<Uri>().unwrap());
级联
像koajs一样,中间件通过调用next.await
挂起并传递控制权给“下游”。然后在next.await
返回时,控制权流回“上游”。
以下示例响应为“Hello World”,但首先请求通过x-response-time和日志中间件来标记请求开始的时间,然后继续通过端点传递控制权。当一个中间件调用next时,函数挂起并将控制权传递给下一个中间件或端点。端点被调用后,堆栈将展开,每个中间件将被恢复以执行其上游行为。
use roa::{App, Context, Next};
use roa::preload::*;
use tracing::info;
use std::error::Error as StdError;
use std::time::Instant;
#[tokio::main]
async fn main() -> Result<(), Box<dyn StdError>> {
let app = App::new()
.gate(logger)
.gate(x_response_time)
.end("Hello, World");
app.listen("127.0.0.1:8000", |addr| {
info!("Server is listening on {}", addr)
})?
.await?;
Ok(())
}
async fn logger(ctx: &mut Context, next: Next<'_>) -> roa::Result {
next.await?;
let rt = ctx.load::<String>("x-response-time").unwrap();
info!("{} {} - {}", ctx.method(), ctx.uri(), rt.as_str());
Ok(())
}
async fn x_response_time(ctx: &mut Context, next: Next<'_>) -> roa::Result {
let start = Instant::now();
next.await?;
let ms = start.elapsed().as_millis();
ctx.store("x-response-time", format!("{}ms", ms));
Ok(())
}
状态处理
您可以捕获或直接抛出next返回的状态。
use roa::{App, Context, Next, status};
use roa::preload::*;
use roa::http::StatusCode;
use tokio::task::spawn;
use tracing::info;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let app = App::new()
.gate(catch)
.gate(not_catch)
.end(status!(StatusCode::IM_A_TEAPOT, "I'm a teapot!"));
app.listen("127.0.0.1:8000", |addr| {
info!("Server is listening on {}", addr)
})?
.await?;
Ok(())
}
async fn catch(_ctx: &mut Context, next: Next<'_>) -> roa::Result {
// catch
if let Err(status) = next.await {
// teapot is ok
if status.status_code != StatusCode::IM_A_TEAPOT {
return Err(status);
}
}
Ok(())
}
async fn not_catch(ctx: &mut Context, next: Next<'_>) -> roa::Result {
next.await?; // just throw
unreachable!()
}
status_handler
应用有一个status_handler来处理顶级中间件抛出的状态。这就是status_handler
use roa::{Context, Status};
pub fn status_handler<S>(ctx: &mut Context<S>, status: Status) {
ctx.resp.status = status.status_code;
if status.expose {
ctx.resp.write(status.message);
} else {
tracing::error!("{}", status);
}
}
路由器。
Roa提供了一个可配置且可嵌套的路由器。
use roa::preload::*;
use roa::router::{Router, get};
use roa::{App, Context};
use tokio::task::spawn;
use tracing::info;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let router = Router::new()
.on("/:id", get(end)); // get dynamic "/:id"
let app = App::new()
.end(router.routes("/user")?); // route with prefix "/user"
app.listen("127.0.0.1:8000", |addr| {
info!("Server is listening on {}", addr)
})?
.await?;
Ok(())
}
async fn end(ctx: &mut Context) -> roa::Result {
// get "/user/1", then id == 1.
let id: u64 = ctx.must_param("id")?.parse()?;
// do something
Ok(())
}
查询
Roa提供了一个中间件 query_parser
。
use roa::preload::*;
use roa::query::query_parser;
use roa::{App, Context};
use tokio::task::spawn;
use tracing::info;
async fn must(ctx: &mut Context) -> roa::Result {
// request "/?id=1", then id == 1.
let id: u64 = ctx.must_query("id")?.parse()?;
Ok(())
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let app = App::new()
.gate(query_parser)
.end(must);
app.listen("127.0.0.1:8080", |addr| {
info!("Server is listening on {}", addr)
})?
.await?;
Ok(())
}
其他模块
- body: 更方便地处理正文。
- compress: 支持透明的内容压缩。
- cookie: cookie获取器或设置器。
- cors: CORS支持。
- forward: "X-Forwarded-*" 解析器。
- jwt: JSON Web Token 支持。
- logger: 日志中间件。
- tls: HTTPS 支持。
- websocket: WebSocket 支持。
依赖
~8-25MB
~414K SLoC