【发布时间】:2017-08-13 21:34:35
【问题描述】:
我正在尝试将 JSON 反序列化为包含可选字段 authorization 的结构。 JSON 可能包含也可能不包含此字段。如果它确实包含该字段,我将自定义反序列化为hyper::header::Authorization<hyper::header::Scheme>。因为Authorization 需要Scheme 的泛型类型,所以我需要(正如我所写的那样)在我的结构中包含泛型类型。
所有测试都通过了,但最后一个(de_json_none,用于 JSON 没有授权字段的测试)在语义上很奇怪,因为我必须以明确的 Scheme 为目标变量类型(Bearer 或 Basic),尽管从 Rust 的角度来看是完全有效的,但这两者对于该数据都没有任何意义。
很清楚为什么会这样,但这是我不想要的,而且我不知道如何解决。
我想通过将数据类型设置为Headers<Bearer> 来编写仅匹配包含Authorization<Bearer> 类型的授权字段的数据的Rocket 处理程序。目前,它还会匹配根本没有该字段的数据。我也没有明确的方法来专门按类型调用缺少字段的数据。
我正在寻找有关如何重构此代码的建议,以反映 Headers 确实具有三个不同的、互斥的化身(Basic、Bearer 和 None)这一事实。也许我应该在这里用枚举做点什么?
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);
}
}
【问题讨论】:
-
虽然我很欣赏代码格式编辑,@Shepmaster,但我发布的代码是由 rustfmt 使用其默认设置格式化的,这应该符合 Rust 样式指南。如果你所做的确实是“标准的 Rust 缩进和样式”,我应该向他们报告吗?
-
我的过程是复制代码,粘贴到the playground,然后点击“格式化”,运行最新发布的rustfmt版本(貌似是
0.9.0)。您使用的是哪个版本的 rustfmt? -
0.7.1。我想是时候升级了!