1 个不稳定版本
0.1.0 | 2020年6月30日 |
---|
#1450 在 编码
33KB
568 行
Tower Web Protobuf
为tower-web提供 Protobuf 中间件及其相关工具。
快速示例
extern crate prost;
#[macro_use]
extern crate tower_web;
extern crate tower_web_protobuf;
use prost::Message;
use tower_web::ServiceBuilder;
use tower_web_protobuf::{Proto, ProtobufMiddleware};
// Messages:
#[derive(Clone, PartialEq, Message, Serialize, Deserialize)]
pub struct Hello {
#[prost(string, tag = "1")]
pub name: String,
}
#[derive(Clone, Debug)]
struct HelloWorld;
type In<M> = Proto<M>;
type Out<M> = Result<Proto<M>, ()>;
impl_web! {
impl HelloWorld {
// You can provide this endpoint with either a Protobuf or JSON
// encoded body. The endpoint will respond with a Protobuf or JSON
// encoded `Hello` message depending on the Accept header.
#[get("/hello/")]
fn hello(&self, hello: In<Hello>) -> Out<Hello> {
Ok(hello)
}
}
}
pub fn main() {
let addr = "127.0.0.1:8080".parse().expect("Invalid address");
ServiceBuilder::new()
.resource(HelloWorld)
// (true, true) permits JSON decoding (requests) and encoding
// (responses)
.middleware(ProtobufMiddleware::new(true, true))
.run(&addr)
.unwrap();
}
它是如何工作的?
tower-web-protobuf
提供了一个通用的 Proto
类型,它可以包装任何 Protobuf 消息结构(实际上,任何实现了 prost 的 Message
类型的对象 - 不幸的是,这意味着您必须与这个包一起使用 prost)。
Proto
类型有一个自定义的 Extract
和 Response
实现,用于处理序列化和反序列化 Protobuf 消息。默认情况下,这些实现将尝试将所有传入的消息解析为 Protobuf,并将所有响应编码为 Protobuf,而不管相应的请求发送了什么内容类型和接受头。
为了真正遵守这些头信息,我们需要引入一些中间件。目前,中间件允许您在编码/解码时启用/禁用 JSON 支持(如上所示)。
明确来说,您只需使用 Proto
类型,并完全忽略中间件。如果您的用例仅涉及发送和接收 Protobuf(没有 JSON),这可能甚至更理想。
还有一个基于 serde-plain 的文本序列化和反序列化选项。
使用方法
首先,将 tower-web-protobuf
添加到您的 Cargo.toml 文件中
[dependencies]
tower-web-protobuf = { git = "https://github.com/rrbutani/tower-web-protobuf" }
为了使用 Proto
类型,您的消息结构必须实现 MessagePlus
中的特性。这意味着实现 prost 的 Message
以及 serde 的 DeserializeOwned
和 Serialize
。对于大多数用例,这意味着使用 prost 并为 serde 的特性添加 #[derive]
属性。
使用 prost,主要有两种方式来完成这个任务:添加一个 build.rs 文件(如这个)使用 prost_build,或者添加 #[derive]
到现有的结构体中,将它们转换为 protobuf 消息。
如果您选择第一条路线,请确保添加以下内容以推导出您消息的serde特性:
prost_build::Config::new()
.type_attribute(".", "#[derive(Serialize, Deserialize)]")
.type_attribute(".", "#[serde(rename_all = \"snake_case\")]")
.type_attribute(".", "#[serde(deny_unknown_fields)]")
.compile_protos(
proto_files_in_dir(MESSAGE_DIR).as_slice(),
&[MESSAGE_DIR.into()],
)
.unwrap();
build.rs 包含上述代码的上下文,而 endpoints 示例 包含一个示例用法。
另一种方法的示例显示在 上面的示例 中,它与 identity 示例 相同。关键部分是消息上的 #[derive]
。
最后,用 Proto
(或使用上面显示的 In
和 Out
类型别名)包装您的消息类型,如果您愿意,还可以添加 ProtobufMiddleware
。它可能 会 好用。
错误处理并不出色,序列化和反序列化错误的日志记录也还未到位。如您所料,如果您打算将此用于重要的事情,请做好出现问题的准备。
<todo: 将文档替换为 docs.rs 链接,从 crates.io 添加许可证,添加 crates.io 链接> <todo: 将所有结构链接到官方 docs.rs 文档页面>
依赖项
~27MB
~500K SLoC