Rust HTTP请求处理库http-serde-ext的使用:增强serde的HTTP序列化与反序列化功能

Rust HTTP请求处理库http-serde-ext的使用:增强serde的HTTP序列化与反序列化功能

简介

http-serde-ext提供了对http crate中类型的serde序列化和反序列化支持,包括:

  • Response
  • Request
  • HeaderMap
  • StatusCode
  • Uri
  • Method
  • HeaderName
  • HeaderValue
  • uri::Authority
  • uri::Scheme
  • uri::PathAndQuery
  • Version
  • 通用的HeaderMap<T>(其中T不是HeaderValue)

还支持上述类型包装在以下std容器类型中:

  • Option
  • Result(Ok位置)
  • Vec
  • VecDeque
  • LinkedList
  • HashMap(作为Key,除了HeaderMapRequestResponse;作为Value支持所有类型)
  • BTreeMap(作为Key仅支持HeaderValueStatusCodeVersion;作为Value支持所有类型)
  • HashSet(除了HeaderMapRequestResponse)
  • BTreeSet(仅支持HeaderValueStatusCodeVersion)

安装

在项目目录中运行以下Cargo命令:

cargo add http-serde-ext

或者在Cargo.toml中添加以下行:

http-serde-ext = "1.0.2"

使用示例

这个库旨在与serde的derive功能一起使用。字段应使用适当的#[serde(with = "...")]注解。以下是完整示例:

use std::collections::*;

use http::*;
use serde::{Serialize, Deserialize};

#[derive(Serialize, Deserialize)]
struct MyStruct {
    #[serde(with = "http_serde_ext::response")]
    base: Response<Vec<u8>>,

    #[serde(with = "http_serde_ext::request::option", default)]
    option: Option<Request<String>>,

    #[serde(with = "http_serde_ext::method::vec")]
    vec: Vec<Method>,

    #[serde(with = "http_serde_ext::uri::vec_deque")]
    vec_deque: VecDeque<Uri>,

    #[serde(with = "http_serde_ext::header_map::linked_list")]
    linked_list: LinkedList<HeaderMap>,

    #[serde(with = "http_serde_ext::header_map_generic::hash_map")]
    hash_map: HashMap<String, HeaderMap<String>>,

    #[serde(with = "http_serde_ext::status_code::btree_map_key")]
    btree_map: BTreeMap<StatusCode, i32>,

    #[serde(with = "http_serde_ext::authority::hash_set")]
    hash_set: HashSet<uri::Authority>,
}

这个库也可以用于手动序列化/反序列化类型,例如使用serde_json时:

let uri = http::Uri::default();
let serialized = http_serde_ext::uri::serialize(&uri, serde_json::value::Serializer).unwrap();
let deserialized = http_serde_ext::uri::deserialize(serialized).unwrap();
assert_eq!(uri, deserialized);

let mut responses: Vec<http::Response<()>> = vec![http::Response::default()];
let serialized =
    http_serde_ext::response::vec::serialize(&responses, serde_json::value::Serializer)
        .unwrap();
let mut deserialized: Vec<http::Response<()>> =
    http_serde_ext::response::vec::deserialize(serialized).unwrap();

let original = responses.remove(0).into_parts();
let deserialized = deserialized.remove(0).into_parts();

assert_eq!(original.0.status, deserialized.0.status);
assert_eq!(original.0.version, deserialized.0.version);
assert_eq(original.0.headers, deserialized.0.headers);
assert_eq!(original.1, deserialized.1);

完整示例代码

use std::collections::{BTreeMap, HashMap, HashSet, LinkedList, VecDeque};
use http::{HeaderMap, Method, Request, Response, StatusCode, Uri};
use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize)]
struct ApiResponse {
    #[serde(with = "http_serde_ext::response")]
    response: Response<String>,
    
    #[serde(with = "http_serde_ext::status_code::btree_map_key")]
    status_codes: BTreeMap<StatusCode, &'static str>,
    
    #[serde(with = "http_serde_ext::uri::vec_deque")]
    endpoints: VecDeque<Uri>,
}

fn main() {
    // 创建一个响应示例
    let response = Response::builder()
        .status(StatusCode::OK)
        .header("Content-Type", "application/json")
        .body("{\"message\":\"Hello, World!\"}".to_string())
        .unwrap();

    // 创建状态码映射
    let mut status_codes = BTreeMap::new();
    status_codes.insert(StatusCode::OK, "OK");
    status_codes.insert(StatusCode::NOT_FOUND, "Not Found");
    
    // 创建端点列表
    let mut endpoints = VecDeque::new();
    endpoints.push_back("/api/v1/users".parse().unwrap());
    endpoints.push_back("/api/v1/products".parse().unwrap());

    // 构建我们的结构体
    let api_response = ApiResponse {
        response,
        status_codes,
        endpoints,
    };

    // 序列化为JSON
    let json = serde_json::to_string(&api_response).unwrap();
    println!("Serialized JSON:\n{}", json);

    // 反序列化
    let deserialized: ApiResponse = serde_json::from_str(&json).unwrap();
    println!("Deserialized response status: {}", deserialized.response.status());
}

完整示例demo

下面是一个更完整的示例,展示如何使用http-serde-ext处理HTTP请求和响应:

use std::collections::{BTreeMap, HashMap};
use http::{header, HeaderMap, Method, Request, Response, StatusCode, Uri};
use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize)]
struct ApiRequest {
    #[serde(with = "http_serde_ext::request")]
    request: Request<String>,
    
    #[serde(with = "http_serde_ext::header_map::hash_map")]
    custom_headers: HashMap<String, HeaderMap<String>>,
}

#[derive(Debug, Serialize, Deserialize)]
struct ApiResponse {
    #[serde(with = "http_serde_ext::response")]
    response: Response<String>,
    
    #[serde(with = "http_serde_ext::status_code::btree_map_key")]
    status_mapping: BTreeMap<StatusCode, String>,
}

fn main() {
    // 创建请求示例
    let request = Request::builder()
        .method(Method::POST)
        .uri("/api/v1/data")
        .header("Authorization", "Bearer token")
        .body("{\"data\":\"test\"}".to_string())
        .unwrap();

    // 创建自定义头映射
    let mut custom_headers = HashMap::new();
    let mut headers1 = HeaderMap::new();
    headers1.insert("X-Custom-1", "Value1".parse().unwrap());
    custom_headers.insert("req1".to_string(), headers1);

    // 构建请求结构体
    let api_request = ApiRequest {
        request,
        custom_headers,
    };

    // 序列化请求
    let request_json = serde_json::to_string(&api_request).unwrap();
    println!("Serialized Request:\n{}", request_json);

    // 创建响应示例
    let response = Response::builder()
        .status(StatusCode::CREATED)
        .header("Content-Type", "application/json")
        .header("X-Rate-Limit", "100")
        .body("{\"id\":123}".to_string())
        .unwrap();

    // 创建状态码映射
    let mut status_mapping = BTreeMap::new();
    status_mapping.insert(StatusCode::OK, "Success".to_string());
    status_mapping.insert(StatusCode::CREATED, "Created".to_string());

    // 构建响应结构体
    let api_response = ApiResponse {
        response,
        status_mapping,
    };

    // 序列化响应
    let response_json = serde_json::to_string(&api_response).unwrap();
    println!("Serialized Response:\n{}", response_json);

    // 反序列化请求
    let deserialized_request: ApiRequest = serde_json::from_str(&request_json).unwrap();
    println!(
        "Deserialized Request Method: {}",
        deserialized_request.request.method()
    );

    // 反序列化响应
    let deserialized_response: ApiResponse = serde_json::from_str(&response_json).unwrap();
    println!(
        "Deserialized Response Status: {}",
        deserialized_response.response.status()
    );
}

致谢

这个crate很大程度上受到Kornel的http-serde的启发。


1 回复

Rust HTTP请求处理库http-serde-ext使用指南

http-serde-ext是一个增强Rust中HTTP请求和响应处理的库,它扩展了serde的功能,使其能更好地处理HTTP相关的序列化和反序列化操作。

主要特性

  • 增强HTTP头部的序列化/反序列化
  • 改进HTTP查询参数的序列化/反序列化
  • 简化HTTP请求体和响应体的处理
  • 与主流HTTP框架(如actix-web, warp等)无缝集成

安装

Cargo.toml中添加依赖:

[dependencies]
http-serde-ext = "0.1"
serde = { version = "1.0", features = ["derive"] }

基本使用方法

1. 处理HTTP头部

use http::header::HeaderMap;
use serde::{Deserialize, Serialize};
use http_serde_ext::header::from_headers;

#[derive(Debug, Deserialize)]
struct MyHeaders {
    #[serde(rename = "x-request-id")]
    request_id: String,
    #[serde(rename = "content-type")]
    content_type: Option<String>,
}

fn process_headers(headers: &HeaderMap) {
    let my_headers: MyHeaders = from_headers(headers).unwrap();
    println!("Request ID: {}", my_headers.request_id);
}

2. 处理查询参数

use serde::{Deserialize, Serialize};
use http_serde_ext::query::from_query;

#[derive(Debug, Deserialize)]
struct Pagination {
    page: u32,
    per_page: u32,
    sort: Option<String>,
}

fn process_query(query: &str) {
    let pagination: Pagination = from_query(query).unwrap();
    println!("Page: {}, Per page: {}", pagination.page, pagination.per_page);
}

3. 处理请求体和响应体

use serde::{Deserialize, Serialize};
use http_serde_ext::body::{from_body, to_body};

#[derive(Debug, Serialize, Deserialize)]
struct User {
    id: u64,
    name: String,
    email: String,
}

// 反序列化请求体
fn process_request(body: &[u8]) {
    let user: User = from_body(body).unwrap();
    println!("User: {:?}", user);
}

// 序列化响应体
fn create_response() -> Vec<u8> {
    let user = User {
        id: 1,
        name: "Alice".to_string(),
        email: "alice@example.com".to_string(),
    };
    to_body(&user).unwrap()
}

与actix-web集成示例

use actix_web::{get, post, web, App, HttpServer, HttpResponse};
use serde::{Deserialize, Serialize};
use http_serde_ext::{header::from_headers, query::from_query, body::{from_body, to_body}};

#[derive(Debug, Serialize, Deserialize)]
struct User {
    id: u64,
    name: String,
}

#[get("/users")]
async fn get_users(headers: web::HeaderMap, query: web::Query<String>) -> HttpResponse {
    // 处理头部
    let my_headers: MyHeaders = from_headers(&headers).unwrap();
    
    // 处理查询参数
    let pagination: Pagination = from_query(&query).unwrap();
    
    // 返回响应
    let users = vec![User { id: 1, name: "Alice".to_string() }];
    HttpResponse::Ok()
        .body(to_body(&users).unwrap())
}

#[post("/users")]
async fn create_user(body: web::Bytes) -> HttpResponse {
    // 处理请求体
    let user: User = from_body(&body).unwrap();
    
    HttpResponse::Created()
        .body(to_body(&user).unwrap())
}

高级用法

自定义序列化/反序列化

use serde::{Deserialize, Serialize, de::Deserializer, ser::Serializer};
use http_serde_ext::header::{serialize_header, deserialize_header};

#[derive(Debug)]
struct CustomType(String);

impl Serialize for CustomType {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serialize_header(&self.0, serializer)
    }
}

impl<'de> Deserialize<'de> for CustomType {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let s = deserialize_header(deserializer)?;
        Ok(CustomType(s))
    }
}

注意事项

  1. 错误处理:所有操作都可能失败,请妥善处理Result
  2. 性能考虑:对于大型请求体,考虑使用流式处理
  3. 安全性:始终验证和清理输入数据

http-serde-ext通过扩展serde的功能,大大简化了Rust中HTTP请求和响应的处理流程,特别是在需要与多种HTTP组件交互的复杂应用中。

完整示例Demo

下面是一个完整的actix-web应用示例,展示了如何使用http-serde-ext处理HTTP请求和响应:

use actix_web::{web, App, HttpServer, HttpResponse, Responder};
use serde::{Deserialize, Serialize};
use http_serde_ext::{header::from_headers, query::from_query, body::{from_body, to_body}};

// 定义数据结构
#[derive(Debug, Serialize, Deserialize)]
struct User {
    id: u64,
    name: String,
    email: String,
}

#[derive(Debug, Deserialize)]
struct MyHeaders {
    #[serde(rename = "x-request-id")]
    request_id: String,
}

#[derive(Debug, Deserialize)]
struct Pagination {
    page: u32,
    per_page: u32,
}

// 处理用户列表请求
async fn list_users(
    headers: web::HeaderMap,
    query: web::Query<String>
) -> impl Responder {
    // 解析头部
    let headers: MyHeaders = from_headers(&headers).unwrap();
    println!("Request ID: {}", headers.request_id);

    // 解析查询参数
    let pagination: Pagination = from_query(&query).unwrap();
    println!("Page: {}, Per page: {}", pagination.page, pagination.per_page);

    // 创建响应数据
    let users = vec![
        User {
            id: 1,
            name: "Alice".to_string(),
            email: "alice@example.com".to_string(),
        },
        User {
            id: 2,
            name: "Bob".to_string(),
            email: "bob@example.com".to_string(),
        }
    ];

    // 返回响应
    HttpResponse::Ok()
        .body(to_body(&users).unwrap())
}

// 处理创建用户请求
async fn create_user(body: web::Bytes) -> impl Responder {
    // 解析请求体
    let user: User = from_body(&body).unwrap();
    println!("Received user: {:?}", user);

    // 返回响应
    HttpResponse::Created()
        .body(to_body(&user).unwrap())
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new()
            .route("/users", web::get().to(list_users))
            .route("/users", web::post().to(create_user))
    })
    .bind("127.0.0.1:8080")?
    .run()
    .await
}

这个完整示例展示了:

  1. 使用from_headers处理HTTP头部
  2. 使用from_query处理查询参数
  3. 使用from_body处理请求体
  4. 使用to_body生成响应体
  5. 与actix-web框架的集成

要运行这个示例,请确保在Cargo.toml中添加了必要的依赖:

[dependencies]
actix-web = "4.0"
http-serde-ext = "0.1"
serde = { version = "1.0", features = ["derive"] }
回到顶部