8

我正在尝试将 JSON 反序列化为包含可选字段的结构authorization。JSON 可能包含也可能不包含此字段。如果它确实包含该字段,我将自定义反序列化为hyper::header::Authorization<hyper::header::Scheme>. 因为Authorization需要一个泛型类型Scheme,所以我需要(正如我所写的那样)在我的结构中包含泛型类型。

所有的测试都通过了,但是最后一个( ,没有de_json_none授权字段的 JSON的那个)在语义上很奇怪,因为我必须针对一个具有确定类型的变量(如图所示或),这两者都没有任何意义数据,尽管从 Rust 的角度来看是完全有效的。SchemeBearerBasic

很清楚为什么会这样,但这是我不想要的,我不知道如何解决。

我想编写一个 Rocket 处理程序,它只匹配包含授权字段类型的数据,方法Authorization<Bearer>是将数据类型设置为Headers<Bearer>. 目前,它还将匹配根本没有该字段的数据。我也没有明确的方法来专门按类型调用缺少字段的数据。

我正在寻找有关如何重构此代码的建议,以反映Headers真正具有三个不同、互斥的化身(BasicBearerNone的事实。也许我应该在这里用枚举做一些事情?

extern crate hyper;
extern crate serde;
extern crate serde_json;
#[macro_use]
extern crate serde_derive;

use hyper::header::{Authorization, Header, Raw, Scheme};
use serde::{Deserialize, Deserializer};

#[derive(Debug, Deserialize, PartialEq)]
struct Headers<S>
where
    S: Scheme + 'static,
{
    #[serde(deserialize_with = "auth_header", default = "no_auth")]
    authorization: Option<Authorization<S>>,
    #[serde(rename = ":path")]
    path: String,
}

fn auth_header<'de, D, S>(deserializer: D) -> Result<Option<Authorization<S>>, D::Error>
where
    D: Deserializer<'de>,
    S: Scheme + 'static,
{
    let s = String::deserialize(deserializer)?;
    let auth = Authorization::parse_header(&Raw::from(s.into_bytes()));
    auth.map(|a| Some(a)).map_err(serde::de::Error::custom)
}

fn no_auth<S>() -> Option<Authorization<S>>
where
    S: Scheme + 'static,
{
    None
}

#[cfg(test)]
mod test {
    use hyper::header::{Basic, Bearer};
    use serde_json;
    use super::*;

    #[test]
    fn de_json_basic() {
        let data = r#"{
                        "authorization": "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==",
                        ":path": "/service/",
                        ":method": "GET"
                      }"#;

        let message = Headers {
            authorization: Some(Authorization(Basic {
                username: "Aladdin".to_owned(),
                password: Some("open sesame".to_owned()),
            })),
            path: "/service/".to_owned(),
        };

        let h: Headers<Basic> = serde_json::from_str(data).unwrap();

        assert_eq!(message, h);
    }

    #[test]
    fn de_json_bearer() {
        let data = r#"{
                        "authorization": "Bearer fpKL54jvWmEGVoRdCNjG",
                        ":path": "/service/",
                        ":method": "GET"
                      }"#;

        let message = Headers {
            authorization: Some(Authorization(
                Bearer { token: "fpKL54jvWmEGVoRdCNjG".to_owned() },
            )),
            path: "/service/".to_owned(),
        };

        let h: Headers<Bearer> = serde_json::from_str(data).unwrap();

        assert_eq!(message, h);
    }

    #[test]
    fn de_json_none() {
        let data = r#"{
                        ":path": "/service/",
                        ":method": "GET"
                      }"#;

        let message = Headers {
            authorization: None,
            path: "/service/".to_owned(),
        };

        let h: Headers<Bearer> = serde_json::from_str(data).unwrap();
        // this also works, though neither should ideally
        // let h: Headers<Basic> = serde_json::from_str(data).unwrap();

        assert_eq!(message, h);
    }
}
4

1 回答 1

2

None没有对应的Sometype就没有 a的概念编译器需要知道为这两种情况的值分配多少空间:

struct ReallyBig([u8; 1024]);
struct ReallySmall(u8);

fn main() {
    let mut choice = None; // How much space to allocate?
}

在您的代码中, 的大小Authorization可以取决于为 选择的值S。由于Headers包含Option<Authorization<S>>, 的大小Headers 可以取决于 的选择S

即使你没有得到任何价值,你也必须选择解析成某种特定的类型。也许您稍后会通过构建适当的值手动将其从 a 更改None为 a Some— 如果没有为其分配足够的空间,那将是麻烦!

因此,我看不出您的解决方案将如何工作。类型是静态的——您需要在编译时知道解码 JSON 是否会导致Authorizationor Bearer,而这根本不可能。

通常,我建议您使用带有Box<Scheme>. 这在这里不起作用,因为Scheme它不是对象安全的。

然后,我建议您实现自己的枚举包装Basic或者BoxScheme为此实现。这并不容易工作,因为Scheme::scheme必须返回一个关键字,但您实际上支持两个关键字!

下一步是实现我们自己的Header

extern crate hyper;
extern crate serde;
extern crate serde_json;
#[macro_use]
extern crate serde_derive;

use hyper::header::{Authorization, Header, Raw, Basic, Bearer};
use serde::{Deserialize, Deserializer};
use std::fmt;

#[derive(Debug, Clone, PartialEq)]
enum MyAuthorization {
    Basic(Authorization<Basic>),
    Bearer(Authorization<Bearer>),
}

impl Header for MyAuthorization {
    fn header_name() -> &'static str {
        // Should always be the same header name, right?
        Authorization::<Basic>::header_name()
    }

    fn parse_header(raw: &Raw) -> hyper::error::Result<Self> {
        Authorization::<Basic>::parse_header(raw)
            .map(MyAuthorization::Basic)
            .or_else(|_| {
                Authorization::<Bearer>::parse_header(raw).map(MyAuthorization::Bearer)
            })
    }

    fn fmt_header(&self, f: &mut hyper::header::Formatter) -> fmt::Result {
        match *self {
            MyAuthorization::Basic(ref a) => a.fmt_header(f),
            MyAuthorization::Bearer(ref a) => a.fmt_header(f),
        }
    }
}

#[derive(Debug, Deserialize, PartialEq)]
struct Headers {
    #[serde(deserialize_with = "auth_header", default)]
    authorization: Option<MyAuthorization>,
    #[serde(rename = ":path")]
    path: String,
}

fn auth_header<'de, D>(deserializer: D) -> Result<Option<MyAuthorization>, D::Error>
where
    D: Deserializer<'de>,
{
    let s = String::deserialize(deserializer)?;
    let auth = MyAuthorization::parse_header(&Raw::from(s.into_bytes()));
    auth.map(Some).map_err(serde::de::Error::custom)
}

#[cfg(test)]
mod test {
    use hyper::header::{Basic, Bearer};
    use serde_json;
    use super::*;

    #[test]
    fn de_json_basic() {
        let data = r#"{
                        "authorization": "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==",
                        ":path": "/service/",
                        ":method": "GET"
                      }"#;

        let message = Headers {
            authorization: Some(MyAuthorization::Basic(Authorization(Basic {
                username: "Aladdin".to_owned(),
                password: Some("open sesame".to_owned()),
            }))),
            path: "/service/".to_owned(),
        };

        let h: Headers = serde_json::from_str(data).unwrap();

        assert_eq!(message, h);
    }

    #[test]
    fn de_json_bearer() {
        let data = r#"{
                        "authorization": "Bearer fpKL54jvWmEGVoRdCNjG",
                        ":path": "/service/",
                        ":method": "GET"
                      }"#;

        let message = Headers {
            authorization: Some(MyAuthorization::Bearer(Authorization(
                Bearer { token: "fpKL54jvWmEGVoRdCNjG".to_owned() },
            ))),
            path: "/service/".to_owned(),
        };

        let h: Headers = serde_json::from_str(data).unwrap();

        assert_eq!(message, h);
    }

    #[test]
    fn de_json_none() {
        let data = r#"{
                        ":path": "/service/",
                        ":method": "GET"
                      }"#;

        let message = Headers {
            authorization: None,
            path: "/service/".to_owned(),
        };

        let h: Headers = serde_json::from_str(data).unwrap();

        assert_eq!(message, h);
    }
}

您可能希望与 Hyper 维护人员核实,看看这是否是执行此类操作的预期方式。

于 2017-08-13T22:55:38.433 回答