#rest #http-client #http #http-parser #file-format #http-file #http-request

bin+lib http-rest-file

将 JetBrains .http/.rest HTTP 客户端格式文件解析为模型或将现有模型序列化到文件

2 个版本

0.5.1 2023 年 8 月 4 日
0.5.0 2023 年 8 月 3 日

#1056 in 解析器实现

Download history 43/week @ 2024-04-08 85/week @ 2024-04-15 54/week @ 2024-04-22 73/week @ 2024-04-29 41/week @ 2024-05-06 67/week @ 2024-05-13 63/week @ 2024-05-20 32/week @ 2024-05-27 57/week @ 2024-06-03 71/week @ 2024-06-10 71/week @ 2024-06-17 59/week @ 2024-06-24 40/week @ 2024-07-01 25/week @ 2024-07-08 31/week @ 2024-07-15 39/week @ 2024-07-22

142 每月下载量
rwalk 中使用

MIT 许可证

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