Crate boluo

Source
Expand description

boluo 是一个简单易用的异步网络框架。

§目录

§快速开始

新建项目:

cargo new demo && cd demo

添加依赖:

[dependencies]
boluo = "0.7"
tokio = { version = "1", features = ["full"] }

用以下内容覆盖 src/main.rs

use boluo::response::IntoResponse;
use boluo::route::Router;
use boluo::server::Server;
use tokio::net::TcpListener;

#[tokio::main]
async fn main() {
    let listener = TcpListener::bind("127.0.0.1:3000").await.unwrap();

    let app = Router::new().mount(hello);

    Server::new(listener).run(app).await.unwrap();
}

#[boluo::route("/", method = "GET")]
async fn hello() -> impl IntoResponse {
    "Hello, World!"
}

运行项目:

cargo run

访问服务:

curl http://127.0.0.1:3000/

§服务

Service 特征表示一个接收请求并返回响应的异步函数。

§处理程序

处理程序是一个异步函数,它接受零个或多个提取器作为参数,并返回可以转换为响应的内容。

处理程序如下所示:

use boluo::body::Body;
use boluo::handler::handler_fn;
use boluo::response::IntoResponse;

// 返回空的 `200 OK` 响应的处理程序。
async fn empty() {}

// 返回带有纯文本主体的 `200 OK` 响应的处理程序。
async fn hello() -> &'static str {
    "Hello, World!"
}

// 返回带有请求主体的 `200 OK` 响应的处理程序。
//
// `Body` 实现了 `FromRequest` 特征,可以作为提取器解析请求。并且也实现了
// `IntoResponse` 特征,可以作为响应类型。
async fn echo(body: Body) -> impl IntoResponse {
    body
}

// 使用 `handler_fn` 函数将处理程序转换为 `Service`。
let service = handler_fn(echo);

§提取器

提取器是实现了 FromRequest 特征的类型,可以根据 Request 创建实例。

use std::convert::Infallible;

use boluo::extract::FromRequest;
use boluo::http::{header, HeaderValue};
use boluo::request::Request;

// 从请求头中提取 HOST 的提取器。
struct Host(Option<HeaderValue>);

// 为提取器实现 `FromRequest` 特征。
impl FromRequest for Host {
    type Error = Infallible;

    async fn from_request(req: &mut Request) -> Result<Self, Self::Error> {
        let value = req.headers().get(header::HOST).map(|v| v.to_owned());
        Ok(Host(value))
    }
}

// 在处理程序中使用提取器从请求中提取数据。
async fn using_extractor(Host(host): Host) {
    println!("{host:?}")
}

§响应

任何实现 IntoResponse 特征的类型都可以作为响应。

use boluo::response::Html;
use boluo::response::IntoResponse;

// 返回带有纯文本主体的 `200 OK` 响应的处理程序。
async fn hello() -> &'static str {
    "Hello, World!"
}

// 返回显示 `Hello, World!` 的 HTML 页面的处理程序。
async fn html() -> impl IntoResponse {
    Html("<html><body>Hello, World!</body></html>")
}

§路由

Router 用于设置哪些路径通向哪些服务。

use boluo::handler::handler_fn;
use boluo::route::Router;

#[boluo::route("/f", method = "GET")]
async fn f() -> &'static str {
    "f"
}

let ab = Router::new()
    .route("/a", handler_fn(|| async { "a" }))
    .route("/b", handler_fn(|| async { "b" }));

let cd = Router::new()
    .route("/c", handler_fn(|| async { "c" }))
    .route("/d", handler_fn(|| async { "d" }));

Router::new()
    // 路由。
    .route("/a", handler_fn(|| async { "a" }))
    .route("/b", handler_fn(|| async { "b" }))
    // 嵌套路由。
    .scope("/x", ab)
    // 将其他路由器的路由合并到当前路由器。
    .merge(cd)
    // 挂载宏定义路由。
    .mount(f);

§错误处理

错误和响应是分离的,可以在中间件对特定错误进行捕获,并将错误转换为自己想要的响应格式。

未经处理的错误到达服务器时,服务器将返回带有错误信息的 500 INTERNAL_SERVER_ERROR 响应。

use boluo::http::StatusCode;
use boluo::response::{IntoResponse, Response};
use boluo::route::{RouteError, RouteErrorKind, Router};
use boluo::service::ServiceExt;
use boluo::BoxError;

#[derive(Debug)]
struct MyError;

impl std::fmt::Display for MyError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "some error message")
    }
}

impl std::error::Error for MyError {}

// 处理程序。
#[boluo::route("/", method = "GET")]
async fn throw_error() -> Result<(), MyError> {
    Err(MyError)
}

// 错误处理。
async fn handle_error(err: BoxError) -> Result<Response, BoxError> {
    // 处理框架抛出的路由错误,并自定义响应方式。
    if let Some(e) = err.downcast_ref::<RouteError>() {
        let status_code = match e.kind() {
            RouteErrorKind::NotFound => StatusCode::NOT_FOUND,
            RouteErrorKind::MethodNotAllowed => StatusCode::METHOD_NOT_ALLOWED,
        };
        return Ok((status_code, format!("{status_code}")).into_response()?);
    }
    if let Some(_) = err.downcast_ref::<MyError>() {
        // 记录错误、转为响应等等。
    }
    Err(err)
}

Router::new().mount(throw_error).or_else(handle_error);

§中间件

中间件是实现了 Middleware 特征的类型,可以调用 ServiceExt::with 函数将中间件 应用于 Service

中间件实际上是将原始服务转换为新的服务。

use boluo::data::Extension;
use boluo::handler::handler_fn;
use boluo::service::ServiceExt;

async fn extension(Extension(text): Extension<&'static str>) -> &'static str {
    text
}

let service = handler_fn(extension);
let service = service.with(Extension("Hello, World!"));

Re-exports§

pub use headers;

Modules§

body
HTTP 主体。
data
通用数据类型。
extract
从请求中提取数据的类型和特征。
handler
可用于处理请求并返回响应的异步函数。
http
http 库的重新导出。
listener
监听器的特征和相关类型的定义。
middleware
中间件的特征和相关类型的定义。
multipartmultipart
用于解析文件上传中常用的 multipart/form-data 格式数据。
request
HTTP 请求。
response
HTTP 响应。
route
将请求转发到服务的类型和特征。
serverhttp1 or http2
HTTP 服务器。
service
服务的特征和相关类型的定义。
static_filestatic-file
静态文件服务。
upgrade
HTTP 升级。
wsws
处理 WebSocket 连接。

Type Aliases§

BoxError
类型擦除的错误类型别名

Attribute Macros§

route
为处理程序添加请求路径和方法。