#http #声明式 #客户端 #异步 #Web #API客户端

pretend

pretend 是一个基于宏的模块化、受 Feign 启发的 HTTP 客户端

7个不稳定版本

0.4.0 2023年2月22日
0.3.0 2021年8月14日
0.2.2 2021年8月11日
0.2.1 2021年6月27日
0.1.1 2021年5月12日

#178 in HTTP客户端

Download history 187/week @ 2024-03-13 110/week @ 2024-03-20 113/week @ 2024-03-27 96/week @ 2024-04-03 55/week @ 2024-04-10 40/week @ 2024-04-17 107/week @ 2024-04-24 64/week @ 2024-05-01 69/week @ 2024-05-08 77/week @ 2024-05-15 73/week @ 2024-05-22 103/week @ 2024-05-29 48/week @ 2024-06-05 59/week @ 2024-06-12 80/week @ 2024-06-19 181/week @ 2024-06-26

每月372次下载
用于 8 个 crate(5 个直接使用)

MIT 许可证

47KB
637

pretend

pretend 是一个模块化、受 Feign 启发的基于宏的 HTTP 客户端。其目标是解耦 REST API 的定义与实现。

一些功能

  • 声明式
  • 以异步为首要实现
  • 与HTTP客户端无关
  • 由于 serde 支持JSON

入门

通过注释一个 trait 来描述 REST API

use pretend::{pretend, request, Result};

#[pretend]
trait HttpBin {
    #[request(method = "POST", path = "/anything")]
    async fn post_anything(&self, body: &'static str) -> Result<String>;
}

在内部,pretend 会为 Pretend 实现这个 trait。可以通过传递客户端实现和 REST API 的基础 URL 来构建这个 struct 的实例。在下面的示例中,我们使用基于 reqwest 的客户端。

use pretend::{Pretend, Url};
use pretend_reqwest::Client;

#[tokio::main]
async fn main() {
    let client = Client::default();
    let url = Url::parse("https://httpbin.org").unwrap();
    let pretend = Pretend::for_client(client).with_url(url);
    let response = pretend.post_anything("hello").await.unwrap();
    assert!(response.contains("hello"));
}

发送头信息、查询参数和正文

使用 header 作为属性提供头信息。

use pretend::{header, pretend, request, Result};

#[pretend]
trait HttpBin {
    #[request(method = "GET", path = "/get")]
    #[header(name = "X-Test-Header-1", value = "abc")]
    #[header(name = "X-Test-Header-2", value = "other")]
    async fn get_with_headers(&self, value: i32, custom: &str) -> Result<()>;
}

查询参数和正文作为方法参数提供。正文类型根据参数名称猜测

  • 参数 body 将作为原始字节数据发送。这要求正文具有 'static 生命周期。
  • 参数 form 将使用 serde 序列为表单编码。
  • 参数 json 将使用 serde 序列为 JSON。

查询参数通过 query 参数传递。它也使用 serde 序列化。

use pretend::{pretend, request, Json, Result, Serialize};

#[derive(Serialize)]
struct Data {
    value: i32,
}

#[pretend]
trait HttpBin {
    #[request(method = "POST", path = "/anything")]
    async fn post_bytes(&self, body: &'static [u8]) -> Result<()>;

    #[request(method = "POST", path = "/anything")]
    async fn post_string(&self, body: &'static str) -> Result<()>;

    #[request(method = "POST", path = "/anything")]
    async fn post_with_query_params(&self, query: &Data) -> Result<()>;

    #[request(method = "POST", path = "/anything")]
    async fn post_json(&self, json: &Data) -> Result<()>;
}

处理响应

pretend 支持广泛的各种响应类型,基于方法的返回类型。正文可以作为一个 Vec<u8>、一个字符串或者通过使用 Json 包装器类型作为 JSON 返回。如果应该丢弃正文,也可以使用单元类型 ()

还提供了 JsonResult 作为便利类型。它将根据 HTTP 状态码反序列化为值类型或错误类型。

当仅检索主体时,HTTP错误会导致方法返回错误。可以通过将这些类型包装在Response中,防止方法失败并访问HTTP状态码。这还允许访问响应头。

use pretend::{pretend, request, Deserialize, Json, JsonResult, Response, Result};

#[derive(Deserialize)]
struct Data {
    value: i32,
}

#[derive(Deserialize)]
struct Error {
    error: String,
}

#[pretend]
trait HttpBin {
    #[request(method = "POST", path = "/anything")]
    async fn read_bytes(&self) -> Result<Vec<u8>>;

    #[request(method = "POST", path = "/anything")]
    async fn read_string(&self) -> Result<String>;

    #[request(method = "POST", path = "/anything")]
    async fn read_json(&self) -> Result<Json<Data>>;

    #[request(method = "POST", path = "/anything")]
    async fn read_json_result(&self) -> Result<JsonResult<Data, Error>>;

    #[request(method = "POST", path = "/anything")]
    async fn read_status(&self) -> Result<Response<()>>;
}

模板化

请求路径和头支持模板化。花括号之间的值将被具有相同名称的参数替换。替换使用format!进行,这意味着支持实现Display的任何类型。

use pretend::{header, pretend, request, Deserialize, Json, Pretend, Result};
use pretend_reqwest::Client;
use std::collections::HashMap;

#[derive(Deserialize)]
struct Data {
    url: String,
    headers: HashMap<String, String>,
}

#[pretend]
trait HttpBin {
    #[request(method = "POST", path = "/{path}")]
    #[header(name = "X-{header}", value = "{value}$")]
    async fn read(&self, path: &str, header: &str, value: i32) -> Result<Json<Data>>;
}

#[tokio::main]
async fn main() {
    let client = Client::default();
    let url = Url::parse("https://httpbin.org").unwrap();
    let pretend = Pretend::for_client(client).with_url(url);
    let response = pretend.read("anything", "My-Header", 123).await.unwrap();
    let data = response.value();
    assert_eq!(data.url, "https://httpbin.org/anything");
    assert_eq!(*data.headers.get("X-My-Header").unwrap(), "123$".to_string());
}

文档

更多信息,请参阅API参考

依赖项

~7–9.5MB
~262K SLoC