14个版本

0.6.0 2024年5月5日
0.5.3 2022年3月15日
0.5.2 2021年9月30日

#29HTTP客户端

Download history 20015/week @ 2024-05-02 18112/week @ 2024-05-09 15175/week @ 2024-05-16 15639/week @ 2024-05-23 16829/week @ 2024-05-30 17960/week @ 2024-06-06 17988/week @ 2024-06-13 16315/week @ 2024-06-20 16077/week @ 2024-06-27 16751/week @ 2024-07-04 17774/week @ 2024-07-11 18241/week @ 2024-07-18 20077/week @ 2024-07-25 21428/week @ 2024-08-01 20241/week @ 2024-08-08 21824/week @ 2024-08-15

87,622 每月下载量
用于 12 个Crate (6个直接)

MIT 许可证

51KB
612

rustify

一个用于与HTTP API端点交互的Rust库

Rustify 是一个用Rust编写的库,它简化了搭建HTTP API的负担。它提供了一个 Endpoint 特征和一个宏助手,允许模板化各种远程端点。它还提供了异步和同步客户端,用于执行针对端点的请求,同时可以选择使用 Client 特征实现自定义客户端。

Rustify 提供了对请求序列化和响应反序列化的支持。还支持以字节形式存在的原始请求和响应。该库还包含了许多用于处理请求的助手,例如中间件支持和包装API响应。

安装

将 rustify 添加为 cargo.toml 中的依赖项

[dependencies]
rustify = "0.6.0"
rustify_derive = "0.5.2"

用法

基本用法

use rustify::{Client, Endpoint};
use rustify_derive::Endpoint;

// Defines an API endpoint at /test/path that takes no inputs and returns an
// empty response.
#[derive(Endpoint)]
#[endpoint(path = "test/path")]
struct Test {}

let endpoint = Test {};
let client = Client::default("http://api.com"); // Configures base address of http://api.com
let result = endpoint.exec(&client).await; // Sends GET request to http://api.com/test/path

assert!(result.is_ok());

请求体

use derive_builder::Builder;
use rustify::{Client, Endpoint};
use rustify_derive::Endpoint;

// Defines an API endpoint at /test/path/{name} that takes one input for
// creating the url and two inputs for building the request body. The content
// type of the request body defaults to JSON, however, it can be modified by
// passing the `request_type` parameter to the endpoint configuration.
//
// Note: The `#[endpoint(body)]` attribute tags are technically optional in the
// below example. If no `body` attribute is found anywhere then rustify defaults
// to serializing all "untagged" fields as part of the body. Fields can be opted
// out of this behavior by tagging them with #[endpoint(skip)].
#[derive(Builder, Endpoint)]
#[endpoint(path = "test/path/{self.name}", method = "POST", builder = "true")]
#[builder(setter(into))] // Improves the building process
struct Test {
    #[endpoint(skip)] // This field shouldn't be serialized anywhere
    pub name: String, // Used to create a dynamic URL
    #[endpoint(body)] // Instructs rustify to serialize this field as part of the body
    pub age: i32,
    #[endpoint(body)]
    pub role: String,
}

// Setting `builder` to true creates a `builder()` method on our struct that
// returns the TestBuilder type created by `derive_builder`.
let endpoint = Test::builder()
        .name("jmgilman")
        .age(42)
        .role("CEO")
        .build()
        .unwrap();
let client = Client::default("http://api.com");
let result = endpoint.exec(&client).await; // Sends POST request to http://api.com/test/path/jmgilman

assert!(result.is_ok());

查询参数

use derive_builder::Builder;
use rustify::{Client, Endpoint};
use rustify_derive::Endpoint;

// Defines a similar API endpoint as in the previous example but adds an
// optional query parameter to the request. Additionally, this example opts to
// not specify the `#[endpoint(body)]` attributes to make use of the default
// behavior covered in the previous example.
#[derive(Builder, Endpoint)]
#[endpoint(path = "test/path/{self.name}", method = "POST", builder = "true")]
#[builder(setter(into, strip_option), default)] // Improves building process
struct Test {
    #[endpoint(skip)]
    pub name: String,
    #[endpoint(query)]
    pub scope: Option<String>, // Note: serialization is skipped when this field is None
    pub age: i32, // Serialized into the request body
    pub role: String, // Serialized into the request body
}

let endpoint = Test::builder()
        .name("jmgilman")
        .scope("global")
        .age(42)
        .role("CEO")
        .build()
        .unwrap();
let client = Client::default("http://api.com");
let result = endpoint.exec(&client).await; // Sends POST request to http://api.com/test/path/jmgilman?scope=global

assert!(result.is_ok());

响应

use rustify::{Client, Endpoint};
use rustify_derive::Endpoint;

// Defines an API endpoint at /test/path that takes a single byte array which
// will be used as the request body (no serialization occurs). The endpoint
// returns a `TestResponse` which contains the result of the operation.
#[derive(Endpoint)]
#[endpoint(path = "test/path", response = "TestResponse")]
struct Test {
    #[endpoint(raw)] // Indicates this field contains the raw request body
    pub file: Vec<u8>
}

#[derive(Deserialize)]
struct TestResponse {
    pub success: bool,
}

let endpoint = Test {
    file: b"contents".to_vec(),
};
let client = Client::default("http://api.com");
let result = endpoint.exec(&client).await;

assert!(result.is_ok());

let response = result.unwrap().parse().unwrap(); // Returns the parsed `TestResponse`
dbg!(response.success);

示例

您可以在 示例 目录中找到示例用法。它们可以使用 cargo 运行

cargo run --package rustify --example reqres1
cargo run --package rustify --example reqres2

vaultrs Crate 是基于 rustify 构建的,可以作为良好的参考。

功能

此Crate提供以下功能

  • blocking:启用 Client 的阻塞变体以及 Endpoint 中的阻塞 exec 函数。

错误处理

此Crate生成的所有错误都封装在由该Crate提供的 ClientError 枚举中。

测试

有关测试,请参阅 测试 目录。使用 cargo test 运行测试。

贡献

查看需要关注的问题,或提交您的问题,然后

  1. 将其分叉(https://github.com/jmgilman/rustify/fork
  2. 创建您的功能分支(git checkout -b feature/fooBar)
  3. 提交您的更改(git commit -am '添加一些fooBar')
  4. 将更改推送到分支(git push origin feature/fooBar)
  5. 创建新的拉取请求

依赖

~6–21MB
~324K SLoC