2 个版本
0.5.1 | 2023 年 8 月 4 日 |
---|---|
0.5.0 | 2023 年 8 月 3 日 |
#1056 in 解析器实现
142 每月下载量
在 rwalk 中使用
190KB
4.5K SLoC
Http Rest File
本项目是 Rust 编写的用于 .http 和 .rest 文件格式的递归下降解析器和生成器。.http/.rest 文件格式描述了由 API 客户端执行的带有目标、头和体的请求,用于测试目的。
JetBrains 在其编辑器中内置了可以执行这些指定请求的 HTTP 客户端。更多信息请参阅:[IntelliJ Http 语法](https://www.jetbrains.com/help/idea/exploring-http-syntax.html)
什么是 .http/.rest 格式
使用 http 文件格式,您可以指定以下请求
- HTTP 方法,例如 (GET, POST, PUT, ...)
- URL
- 可选的 HTTP 版本
- 头部
- 正文
此类请求可能如下所示
GET https://httpbin.org/get
或使用 POST 并在注释中添加一些额外的元信息
### Some comment describing the request
# @name=Request Name
# @no-log @no-follow
POST https://httpbin.org/post
Content-Type: application/json
< path/to/json/file.json
>>! save_response_output.json
JetBrains 还在以下 GitHub 项目中指定了编辑器中的请求。[http-request-in-editor-spec](https://github.com/JetBrains/http-request-in-editor-spec) 规范的内容似乎有些过时,但仍然很好地描述了格式。
使用 Cargo 添加库
cargoadd http-rest-file
用法
如果您正在使用此软件包,您希望将 .http/.rest 文件的内容解析为模型,或者反过来,从现有模型创建文件内容。
模型
请求模型如下所示
pub struct Request {
pub name: Option<String>,
pub comments: Vec<Comment>,
pub request_line: RequestLine,
pub headers: Vec<Header>,
pub body: RequestBody,
pub settings: RequestSettings,
pub pre_request_script: Option<PreRequestScript>,
pub response_handler: Option<ResponseHandler>,
pub save_response: Option<SaveResponse>,
}
其中一些部分包含在注释(元信息)中,例如 name
以及 settings
(@no-log
, @no-cookie-jar
, @no-follow
)。请求行包含 HTTP 方法类型、URL 以及可选的 HTTP 版本。请求前的脚本和响应处理程序是可选的,不需要存在。可选地,响应的结果可以发送到由 SaveResponse
类型指定的文件。
文本解析 -> 模型
根据文件路径解析文件
use http_rest_file::Parser;
use std::path::PathBuf;
fn main() {
let model = Parser::parse_file(PathBuf::from("./your/path/request.http")).expect(jj)
}
解析字符串内容
let str = r#####"
POST http://example.com/api/add
Content-Type: application/json
< ./input.json
###
GET https://example.com/first
###
GET https://example.com/second
###
"#####;
let FileParseResult { requests, errs } = dbg!(Parser::parse(str, false));
println!("errs: {:?}", errs);
assert_eq!(errs.len(), 1);
assert_eq!(requests.len(), 3);
// @TODO check content
assert_eq!(
requests,
vec![
model::Request {
name: None,
comments: vec![],
headers: vec![Header {
key: "Content-Type".to_string(),
value: "application/json".to_string()
}],
body: model::RequestBody::Raw {
data: DataSource::FromFilepath("./input.json".to_string())
},
request_line: model::RequestLine {
http_version: WithDefault::default(),
method: WithDefault::Some(HttpMethod::POST),
target: model::RequestTarget::Absolute {
uri: "http://example.com/api/add".to_string()
}
},
settings: RequestSettings::default(),
pre_request_script: None,
response_handler: None,
save_response: None,
},
model::Request {
name: None,
comments: vec![],
headers: vec![],
body: model::RequestBody::None,
request_line: model::RequestLine {
http_version: WithDefault::default(),
method: WithDefault::Some(HttpMethod::GET),
target: model::RequestTarget::Absolute {
uri: "https://example.com/first".to_string()
}
},
settings: RequestSettings::default(),
pre_request_script: None,
response_handler: None,
save_response: None,
},
model::Request {
name: None,
comments: vec![],
headers: vec![],
body: model::RequestBody::None,
request_line: model::RequestLine {
http_version: WithDefault::default(),
method: WithDefault::Some(HttpMethod::GET),
target: model::RequestTarget::Absolute {
uri: "https://example.com/second".to_string()
}
},
settings: RequestSettings::default(),
pre_request_script: None,
response_handler: None,
save_response: None
}
],
);
}
序列化模型 -> 文本
可以将模型序列化到文件,前提是提供了一个 HttpRestFile
模型: http_rest_file::Serializer::serialize_to_file(rest_file_model)
或者可以将模型序列化到字符串,前提是提供了一个请求模型列表,每个请求都会被序列化,请求之间通过请求分隔符分隔,分隔符是三个标签:###
http_rest_file::序列化器::serialize_requests(requests)
完整示例
#[test]
pub fn serialize_with_headers() {
let request = Request {
name: None,
headers: vec![Header::new("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36")
, Header::new("Accept-Language", "en-US,en;q=0.9,es;q=0.8"),
// fake token
Header::new("Authorization", "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"),
Header::new("Cache-Control", "max-age=3600")
],
comments: vec![],
settings: RequestSettings {
no_redirect: None,
no_log: None,
no_cookie_jar: None,
},
request_line: RequestLine {
method: WithDefault::Some(HttpMethod::POST),
target: RequestTarget::from("https://httpbin.org/post"),
http_version: WithDefault::default(),
},
body: RequestBody::None,
pre_request_script: None,
response_handler: None,
save_response: None,
};
// we expect a newline after the headers
let expected = r"POST https://httpbin.org/post
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36
Accept-Language: en-US,en;q=0.9,es;q=0.8
Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Cache-Control: max-age=3600
";
let serialized = Serializer::serialize_requests(&[&request]);
assert_eq!(serialized, expected);
}
错误处理和恢复
在解析过程中可能会发生错误。有关可能发生的错误类型及其显示的消息,请参阅 ParseError
枚举类型。
如果请求可以部分解析,则返回 ErrorWithPartial
,其中包含一个 PartialRequest
,其中包含错误发生之前可以解析的所有部分。从 PartialRequest
中,您可以创建一个 Request
模型,其中包含所有成功解析的部分,其余部分使用默认值填充。
有关错误发生位置更详细的信息,ParseErrorDetails
类型包含解析字符串中的光标位置。
依赖关系
~4–7MB
~155K SLoC