#protobuf #middleware #friends #tower #serialization #json #tower-web

tower-web-protobuf

为 tower-web 提供帮助处理 Protobuf 的中间件及其相关工具

1 个不稳定版本

0.1.0 2020年6月30日

#1450编码

MIT 许可协议

33KB
568

Tower Web Protobuf

tower-web提供 Protobuf 中间件及其相关工具。

Build Status Docs Coverage Status License: MIT

快速示例

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 类型有一个自定义的 ExtractResponse 实现,用于处理序列化和反序列化 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 的 DeserializeOwnedSerialize。对于大多数用例,这意味着使用 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(或使用上面显示的 InOut 类型别名)包装您的消息类型,如果您愿意,还可以添加 ProtobufMiddleware。它可能 好用。

错误处理并不出色,序列化和反序列化错误的日志记录也还未到位。如您所料,如果您打算将此用于重要的事情,请做好出现问题的准备。

<todo: 将文档替换为 docs.rs 链接,从 crates.io 添加许可证,添加 crates.io 链接> <todo: 将所有结构链接到官方 docs.rs 文档页面>

依赖项

~27MB
~500K SLoC