39 个版本
0.9.0 | 2024 年 5 月 14 日 |
---|---|
0.8.4 | 2023 年 9 月 12 日 |
0.8.3 | 2023 年 3 月 20 日 |
0.8.0 | 2022 年 11 月 7 日 |
0.0.1 | 2019 年 10 月 27 日 |
#47 在 HTTP 服务器 中
每月 31 次下载
145KB
3K SLoC
gotham-restful

这个 crate 是一个针对流行的 Rust gotham Web 框架 的扩展。它允许你创建具有分配端点的资源,旨在提供创建请求处理程序的一种更便捷的方式。
特性
- 自动解析 JSON 请求并生成响应体
- 允许使用 原始 请求和响应体
- 方便的 宏 用于创建可以注册到 gotham 路由器上的响应
- 自动生成 API 的 OpenAPI 规范
- 管理 CORS 头部,无需手动操作
- 使用 JWT 管理认证
- 集成 diesel 连接池,以便轻松进行 数据库 集成
安全性
这个 crate 与任何用安全 Rust 编写的代码一样安全,并且 #![forbid(unsafe_code)]
确保没有使用不安全代码。
端点
存在一系列预定义端点,应该可以覆盖大多数 REST API。然而,也可以定义自己的端点。
预定义端点
假设你将 /foobar
分配给你的资源,以下预定义端点存在
端点名称 | 必需参数 | HTTP 动词 | HTTP 路径 |
---|---|---|---|
read_all | GET | /foobar | |
read | id | GET | /foobar/:id |
search | query | GET | /foobar/search |
create | body | POST | /foobar |
update_all | body | PUT | /foobar |
更新 | id, body | PUT | /foobar/:id |
delete_all | DELETE | /foobar | |
删除 | id | DELETE | /foobar/:id |
每个端点都有一个宏来为资源创建必要的模板代码。一个简单的例子如下:
/// Our RESTful resource.
#[derive(Resource)]
#[resource(read)]
struct FooResource;
/// The return type of the foo read endpoint.
#[derive(Serialize)]
struct Foo {
id: u64
}
/// The foo read endpoint.
#[read]
fn read(id: u64) -> Success<Foo> {
Foo { id }.into()
}
自定义端点
使用#[endpoint]
宏来定义自定义端点。其语法与预定义端点类似,但需要提供更多上下文。
use gotham_restful::gotham::hyper::Method;
#[derive(Resource)]
#[resource(custom_endpoint)]
struct CustomResource;
/// This type is used to parse path parameters.
#[derive(Clone, Deserialize, StateData, StaticResponseExtender)]
struct CustomPath {
name: String
}
#[endpoint(
uri = "custom/:name/read",
method = "Method::GET",
params = false,
body = false
)]
fn custom_endpoint(path: CustomPath) -> Success<String> {
path.name.into()
}
参数
一些端点需要参数。这些参数应该是:
- id 应该是可反序列化为 json-primitive 的值,如
i64
或String
。 - body 应该是任何可反序列化的对象,或者任何实现
RequestBody
的类型。 - query 应该是任何可反序列化的对象,其变量是 json-primitive。但是它不会从 json 中解析,而是从 HTTP GET 参数中解析,如
search?id=1
。该类型需要实现QueryStringExtractor
。
此外,所有处理程序都可以接受 gotham 的 State
的引用。请注意,对于异步处理程序,它需要是一个可变引用,直到 rustc 的生命周期检查跨越 await 边界得到改善。
上传和下载
默认情况下,每个请求体都从 json 解析,每个响应都使用 serde_json 转换为 json。但是,您也可以使用原始体。以下是一个示例,其中请求体简单作为响应返回,不涉及 json 解析。
#[derive(Resource)]
#[resource(create)]
struct ImageResource;
#[derive(FromBody, RequestBody)]
#[supported_types(mime::IMAGE_GIF, mime::IMAGE_JPEG, mime::IMAGE_PNG)]
struct RawImage {
content: Vec<u8>,
content_type: Mime
}
#[create]
fn create(body: RawImage) -> Raw<Vec<u8>> {
Raw::new(body.content, body.content_type)
}
自定义 HTTP 头部
您可以从状态中读取请求头部,就像在任何其他 gotham 处理程序中一样,并使用 Response::header 指定自定义响应头部。
#[derive(Resource)]
#[resource(read_all)]
struct FooResource;
#[read_all]
async fn read_all(state: &mut State) -> NoContent {
let headers: &HeaderMap = state.borrow();
let accept = &headers[ACCEPT];
let mut res = NoContent::default();
res.header(VARY, "accept".parse().unwrap());
res
}
特性
为了简化常见用例,此创建提供了一些可能有助于实现您的 Web 服务器的功能。完整的功能列表如下:
auth
高级 JWT 中间件cors
所有端点处理程序的 CORS 处理database
diesel 中间件支持errorlog
记录端点处理程序返回的错误full
启用所有功能,除了without-openapi
openapi
路由添加以生成 openapi 规范without-openapi
(默认) 禁用openapi
支持。
认证功能
要启用认证支持,请启用 auth
功能门。这将允许您注册一个中间件,可以自动检查 JWT 认证令牌的存在。除了支持端点宏外,它还支持使用 JWT 数据查找所需的 JWT 密钥,因此您可以使用多个 JWT 密钥并在运行时决定使用哪个密钥。这些功能目前都不由 gotham 的 JWT 中间件支持。
以下是一个仅使用单个密钥的简单示例:
#[derive(Resource)]
#[resource(read)]
struct SecretResource;
#[derive(Serialize)]
struct Secret {
id: u64,
intended_for: String
}
#[derive(Deserialize, Clone)]
struct AuthData {
sub: String,
exp: u64
}
#[read]
fn read(auth: AuthStatus<AuthData>, id: u64) -> AuthSuccess<Secret> {
let intended_for = auth.ok()?.sub;
Ok(Secret { id, intended_for })
}
fn main() {
let auth: AuthMiddleware<AuthData, _> = AuthMiddleware::new(
AuthSource::AuthorizationHeader,
AuthValidation::default(),
StaticAuthHandler::from_array(b"zlBsA2QXnkmpe0QTh8uCvtAEa4j33YAc")
);
let (chain, pipelines) = single_pipeline(new_pipeline().add(auth).build());
gotham::start(
"127.0.0.1:8080",
build_router(chain, pipelines, |route| {
route.resource::<SecretResource>("secret");
})
)
.expect("Failed to start gotham");
}
CORS 功能
cors功能使得从其他源使用这个Web服务器变得简单。默认情况下,只有Access-Control-Allow-Methods
头被修改。要更改行为,请添加您想要的配置作为中间件。
以下是一个简单示例,允许所有源进行身份验证(注意,*
总是不允许身份验证),以及所有内容类型,如下所示
#[derive(Resource)]
#[resource(read_all)]
struct FooResource;
#[read_all]
fn read_all() {
// your handler
}
fn main() {
let cors = CorsConfig {
origin: Origin::Copy,
headers: Headers::List(vec![CONTENT_TYPE]),
max_age: 0,
credentials: true
};
let (chain, pipelines) = single_pipeline(new_pipeline().add(cors).build());
gotham::start(
"127.0.0.1:8080",
build_router(chain, pipelines, |route| {
route.resource::<FooResource>("foo");
})
)
.expect("Failed to start gotham");
}
cors功能也可以用于非资源处理器。请参阅CorsRoute
的示例。
数据库功能
数据库功能允许轻松地将diesel集成到您的处理器函数中。但是请注意,由于gotham的diesel中间件实现方式,在保持数据库连接的同时无法运行异步代码。如果您需要结合异步和数据库,则需要从State
中借用连接并返回一个boxed future。
以下是一个简单的非异步示例
#[derive(Resource)]
#[resource(read_all)]
struct FooResource;
#[derive(Queryable, Serialize)]
struct Foo {
id: i64,
value: String
}
#[read_all]
fn read_all(conn: &mut PgConnection) -> QueryResult<Vec<Foo>> {
foo::table.load(conn)
}
type Repo = gotham_middleware_diesel::Repo<PgConnection>;
fn main() {
let repo = Repo::new(&env::var("DATABASE_URL").unwrap());
let diesel = DieselMiddleware::new(repo);
let (chain, pipelines) = single_pipeline(new_pipeline().add(diesel).build());
gotham::start(
"127.0.0.1:8080",
build_router(chain, pipelines, |route| {
route.resource::<FooResource>("foo");
})
)
.expect("Failed to start gotham");
}
OpenAPI功能
OpenAPI功能可能是这个crate中最强大的功能之一。作为二进制文件和库作者,务必仔细阅读本节,以避免出现意外。
为了自动创建openapi规范,gotham-restful需要了解所有路由和返回的类型。serde
在序列化方面做得很好,但提供的类型信息不足,因此所有在路由器中使用的类型都需要实现OpenapiType
。这可以用于几乎任何类型,并且通常不需要手动实现。以下是一个简单示例
#[derive(Resource)]
#[resource(read_all)]
struct FooResource;
#[derive(OpenapiType, Serialize)]
struct Foo {
bar: String
}
#[read_all]
fn read_all() -> Success<Foo> {
Foo {
bar: "Hello World".to_owned()
}
.into()
}
fn main() {
gotham::start(
"127.0.0.1:8080",
build_simple_router(|route| {
let info = OpenapiInfo {
title: "My Foo API".to_owned(),
version: "0.1.0".to_owned(),
urls: vec!["https://example.org/foo/api/v1".to_owned()]
};
route.with_openapi(info, |mut route| {
route.resource::<FooResource>("foo");
route.openapi_spec("openapi");
route.openapi_doc("/");
});
})
)
.expect("Failed to start gotham");
}
上述示例在之前添加资源的基础上,还添加了两个其他端点:/openapi
和/
。第一个将返回生成的openapi规范,以JSON格式,允许您轻松生成不同语言中的客户端,而无需在每个语言中精确复制您的api。第二个将返回HTML格式的文档,这样您就可以轻松查看您的api并与他人分享。
陷阱
openapi功能有一些陷阱,您应该注意。
-
结构体的名称在openapi规范中用作“链接”。因此,如果您在项目中有两个具有相同名称的结构体,openapi规范将是无效的,因为其中只有一个将包含在规范中。
-
默认情况下,此crate的
without-openapi
功能已启用。取消启用它以使用openapi
功能将向此crate中的一些特性和类型添加额外的类型界限和方法要求,例如,现在您必须实现EndpointWithSchema
而不是Endpoint
。这意味着某些代码可能仅在其中一个功能上编译,而不是在两个功能上。如果您正在编写使用gotham-restful的库,强烈建议通过两个功能并条件性地启用openapi代码,如下所示#[derive(Deserialize, Serialize)] #[cfg_attr(feature = "openapi", derive(openapi_type::OpenapiType))] struct Foo;
版本控制
与所有Rust crate一样,此crate将遵循语义版本控制指南。但是,更改MSRV(最低支持的Rust版本)不被视为破坏性更改。
许可证
版权(C)2019-2024 Dominic Meiser和贡献者。
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
依赖关系
~11–24MB
~341K SLoC