15个版本 (重大更改)
0.11.0 | 2023年1月21日 |
---|---|
0.10.0 | 2022年1月1日 |
0.9.0 | 2021年12月23日 |
0.8.0 | 2021年6月3日 |
0.7.1 | 2021年2月21日 |
#333 in HTTP服务器
每月48次下载
用于 seamless_macros
77KB
1K SLoC
Seamless
这个库旨在提供一个易于使用且可扩展的方法来声明API路由,并会自动跟踪请求和响应周围的各项信息,从而可以根据API的当前状态生成详细的路由信息(包括类型信息)。
这个库是完全异步的,但与任何特定的异步实现无关(你甚至不需要切换任何功能标志)。
使用Seamless,我们可以用纯Rust编写我们的API,然后根据API的当前状态生成基于TypeScript的API客户端(或只是类型,或只是文档)。这允许我们在不依赖外部规范如OpenAPI的情况下,实现API和类似TypeScript客户端之间的类型安全通信。
使用此库的典型步骤是
- 使用 [
macro@ApiBody
] 宏对这些路由的输入和输出类型进行注释。 - 为任何你希望发出的错误推导 [
macro@ApiError
](或手动实现Into<ApiError>
)。 - 使用此库声明每个API路由。API处理程序只需作为函数参数请求它们需要的内容,包括基于传入请求的任意状态或信息(由你决定)。
- 一旦声明了API路由,就使用 [
Api::info()
] 获取有关API的足够信息以生成完全类型安全的客户端代码(信息已优化以生成TypeScript类型/代码)。 - 将此API与类似
warp
或rocket
的东西集成,以便你的seamless
API路由可以与你要服务的一切内容一起存在(参见示例以了解如何进行此操作)。
查看 examples
目录中的示例,以了解如何使用此库,或继续阅读!
基本示例
以下是一个使用本库的基本自包含示例。
use seamless::{
http::{ Request },
api::{ Api, ApiBody, ApiError },
handler::{ body::FromJson, request::Bytes, response::ToJson }
};
// The API relies on types that have been annotated with `ApiBody` (request and response
// types) or `ApiError` (for any errors we might give back). These annotations do some
// reflection to allow us to get information about the shape of the type and doc comments
// added to it, as well as ensuring that they can be Serialized/Deserialized.
#[ApiBody]
struct DivisionInput {
a: usize,
b: usize
}
#[ApiBody]
#[derive(PartialEq)]
struct DivisionOutput {
a: usize,
b: usize,
result: usize
}
// Any errors that we return must implement `Into<ApiError>`, Display and Debug. We can derive
// `ApiError` to automate this for us. Here we use `thiserror` to derive the Display impl
// for us. See the documentation on the `ApiError` macro for more info.
#[derive(ApiError, Debug, thiserror::Error, PartialEq)]
enum MathsError {
#[error("Division by zero")]
#[api_error(external, code=400)]
DivideByZero
}
let mut api = Api::new();
// We add routes to our new API like so. The handler functions would often be defined
// separately and called from this handler. Handler functions can be async or sync, and can
// return any valid handler::HandlerResponse.
api.add("/echo")
.description("Echoes back a JSON string")
.handler(|body: FromJson<String>| ToJson(body.0));
api.add("/reverse")
.description("Reverse an array of numbers")
.handler(|body: FromJson<Vec<usize>>|
ToJson(body.0.into_iter().rev().collect::<Vec<usize>>())
);
api.add("/maths.divide")
.description("Divide two numbers by each other")
.handler(|body: FromJson<DivisionInput>| async move {
let a = body.0.a;
let b = body.0.b;
a.checked_div(b)
.ok_or(MathsError::DivideByZero)
.map(|result| ToJson(DivisionOutput { a, b, result }))
});
// Once we've added routes to the `api`, we use it by sending `http::Request`s to it.
// Since we're expecting JSON to be provided, we need to remember to set the correct
// content-type:
let req = Request::post("/maths.divide")
.header("content-type", "application/json")
.body(Bytes::from_vec(serde_json::to_vec(&DivisionInput { a: 20, b: 10 }).unwrap()))
.unwrap();
assert_eq!(
api.handle(req).await.unwrap().into_body(),
serde_json::to_vec(&DivisionOutput{ a: 20, b: 10, result: 2 }).unwrap()
);
请查看API文档以获取更多信息,或查看示例。
依赖项
~4MB
~79K SLoC