Rust查询字符串处理库serde-querystring的使用:高效序列化与反序列化URL查询参数

Rust查询字符串处理库serde-querystring的使用:高效序列化与反序列化URL查询参数

安装

# Cargo.toml
[dependencies]
serde-querystring = "0.3.0"

用法

您可以直接使用此crate中提供的解析器,每个解析器的测试中都有示例。

use serde_querystring::DuplicateQS;

let parsed = DuplicateQS::parse(b"foo=bar&foo=baz");
let values = parsed.values(b"foo"); // 将为您提供b"bar"和b"baz"的向量

或者您可以使用serde(使用serde功能,默认启用)

use serde::Deserialize;
use serde_querystring::{from_str, ParseMode, DuplicateQS};

#[derive(Deserialize)]
struct MyStruct{
  foo: Vec<String> // 或(String, u32)元组
}

let parsed: MyStruct = from_str("foo=bar&foo=2022", ParseMode::Duplicate).unwrap();
// 或
let parsed: MyStruct = DuplicateQS::parse(b"foo=bar&foo=baz").deserialize().unwrap();

还有用于actix_webserde-querystring-actix)和axumserde-querystring-axum)的crate,它们为其框架提供提取器,可以在不直接依赖核心crate的情况下使用。

解析器

简单模式

简单地解析键=值对,每个键只接受一个值。如果键重复,我们只收集最后一个值。

use serde_querystring::{UrlEncodedQS, ParseMode, from_str};

UrlEncodedQS::parse(b"key=value");
// 或
let res: MyStruct = from_str("foo=bar&key=value", ParseMode::UrlEncoded).unwrap();

重复键模式

通过重复键支持向量或值。

use serde_querystring::{DuplicateQS, ParseMode, from_str};

DuplicateQS::parse(b"foo=bar&foo=bar2&foo=bar3");
// 或
let res: MyStruct = from_str("foo=bar&foo=bar2&foo=bar3", ParseMode::Duplicate).unwrap();

分隔符模式

通过使用分隔符字节(例如b’|’)支持向量或值。

use serde_querystring::{DelimiterQS, ParseMode, from_str};

DelimiterQS::parse(b"foo=bar|bar2|bar3", b'|');
// 或
let res: MyStruct = from_str("foo=bar|bar2|bar3", ParseMode::Delimiter(b'|')).unwrap();

括号模式

通过使用括号和子键支持向量或值。

use serde_querystring::{BracketsQS, ParseMode, from_str};

BracketsQS::parse(b"foo[1]=bar&foo[2]=bar&foo[3]=bar");
// 或
let res: MyStruct = from_str("foo[1]=bar&foo[2]=bar&foo[3]=bar", ParseMode::Brackets).unwrap();

完整示例代码

use serde::{Deserialize, Serialize};
use serde_querystring::{from_str, to_string, ParseMode};

// 定义数据结构
#[derive(Debug, Serialize, Deserialize)]
struct QueryParams {
    name: String,
    age: u32,
    tags: Vec<String>,
    filters: Option<Vec<String>>,
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 示例查询字符串
    let query_string = "name=John&age=30&tags=rust&tags=web&tags=serde";
    
    // 反序列化:从查询字符串到结构体
    let params: QueryParams = from_str(query_string, ParseMode::Duplicate)?;
    println!("反序列化结果: {:?}", params);
    
    // 序列化:从结构体到查询字符串
    let new_params = QueryParams {
        name: "Alice".to_string(),
        age: 25,
        tags: vec!["programming".to_string(), "backend".to_string()],
        filters: Some(vec!["active".to_string(), "verified".to_string()]),
    };
    
    let serialized = to_string(&new_params, ParseMode::Duplicate)?;
    println!("序列化结果: {}", serialized);
    
    Ok(())
}

// 测试不同解析模式的示例
#[cfg(test)]
mod tests {
    use super::*;
    
    #[test]
    fn test_simple_mode() {
        #[derive(Debug, Deserialize)]
        struct SimpleParams {
            key: String,
            foo: String,
        }
        
        let query = "foo=bar&key=value";
        let params: SimpleParams = from_str(query, ParseMode::UrlEncoded).unwrap();
        assert_eq!(params.foo, "bar");
        assert_eq!(params.key, "value");
    }
    
    #[test]
    fn test_delimiter_mode() {
        #[derive(Debug, Deserialize)]
        struct DelimiterParams {
            foo: Vec<String>,
        }
        
        let query = "foo=bar|baz|qux";
        let params: DelimiterParams = from_str(query, ParseMode::Delimiter(b'|')).unwrap();
        assert_eq!(params.foo, vec!["bar", "baz", "qux"]);
    }
}

完整示例demo

use serde::{Deserialize, Serialize};
use serde_querystring::{from_str, to_string, ParseMode};

// 定义用户查询参数结构体
#[derive(Debug, Serialize, Deserialize)]
struct UserQuery {
    id: u32,                    // 用户ID
    username: String,           // 用户名
    roles: Vec<String>,         // 用户角色列表
    is_active: bool,            // 是否活跃
    page: Option<u32>,          // 页码(可选)
    per_page: Option<u32>,      // 每页数量(可选)
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    println!("=== serde-querystring 完整使用示例 ===");
    
    // 示例1:重复键模式解析
    println!("\n1. 重复键模式解析:");
    let query1 = "id=123&username=john_doe&roles=admin&roles=user&is_active=true&page=1&per_page=20";
    let user1: UserQuery = from_str(query1, ParseMode::Duplicate)?;
    println!("解析结果: {:?}", user1);
    
    // 示例2:分隔符模式解析
    println!("\n2. 分隔符模式解析:");
    let query2 = "id=456&username=jane_smith&roles=moderator|user|guest&is_active=false";
    let user2: UserQuery = from_str(query2, ParseMode::Delimiter(b'|'))?;
    println!("解析结果: {:?}", user2);
    
    // 示例3:简单模式解析(最后一个值生效)
    println!("\n3. 简单模式解析:");
    let query3 = "id=789&username=test_user&roles=admin&roles=user";
    let user3: UserQuery = from_str(query3, ParseMode::UrlEncoded)?;
    println!("解析结果: {:?}", user3);
    
    // 示例4:序列化为查询字符串
    println!("\n4. 序列化为查询字符串:");
    let new_user = UserQuery {
        id: 999,
        username: "new_user".to_string(),
        roles: vec!["admin".to_string(), "editor".to_string()],
        is_active: true,
        page: Some(2),
        per_page: Some(50),
    };
    
    let serialized = to_string(&new_user, ParseMode::Duplicate)?;
    println!("序列化结果: {}", serialized);
    
    // 示例5:处理可选字段
    println!("\n5. 处理可选字段:");
    let query5 = "id=111&username=optional_user&is_active=true";
    let user5: UserQuery = from_str(query5, ParseMode::Duplicate)?;
    println!("解析结果: {:?}", user5);
    println!("页码: {:?}, 每页数量: {:?}", user5.page, user5.per_page);
    
    Ok(())
}

// 错误处理示例
fn handle_query_errors() {
    println!("\n=== 错误处理示例 ===");
    
    // 无效查询字符串
    let invalid_query = "invalid_format";
    match from_str::<UserQuery>(invalid_query, ParseMode::Duplicate) {
        Ok(user) => println!("成功解析: {:?}", user),
        Err(e) => println!("解析错误: {}", e),
    }
    
    // 类型不匹配
    let type_mismatch = "id=not_number&username=test";
    match from_str::<UserQuery>(type_mismatch, ParseMode::Duplicate) {
        Ok(user) => println!("成功解析: {:?}", user),
        Err(e) => println!("类型错误: {}", e),
    }
}

// 运行示例
#[cfg(test)]
mod tests {
    use super::*;
    
    #[test]
    fn test_all_parse_modes() {
        // 测试所有解析模式
        let query = "roles=admin|user|guest";
        
        // 分隔符模式
        let user1: UserQuery = from_str("id=1&username=test&is_active=true", ParseMode::Delimiter(b'|')).unwrap();
        assert_eq!(user1.id, 1);
        
        // 重复键模式
        let user2: UserQuery = from_str("id=2&username=test2&roles=admin&roles=user", ParseMode::Duplicate).unwrap();
        assert_eq!(user2.roles.len(), 2);
        
        // 简单模式
        let user3: UserQuery = from_str("id=3&username=test3&roles=admin&roles=user", ParseMode::UrlEncoded).unwrap();
        assert_eq!(user3.roles.len(), 1); // 只保留最后一个值
    }
    
    #[test]
    fn test_serialization() {
        let user = UserQuery {
            id: 100,
            username: "serialize_test".to_string(),
            roles: vec!["role1".to_string(), "role2".to_string()],
            is_active: true,
            page: None,
            per_page: None,
        };
        
        let serialized = to_string(&user, ParseMode::Duplicate).unwrap();
        assert!(serialized.contains("id=100"));
        assert!(serialized.contains("username=serialize_test"));
    }
}

许可证

此项目根据以下任一许可证授权:

  • Apache License, Version 2.0
  • MIT license

由您选择。


1 回复

serde-querystring:高效的Rust查询字符串处理库

概述

serde-querystring是一个专门用于处理URL查询字符串的Rust库,它基于serde框架提供了高效的序列化和反序列化功能。该库特别适合处理Web应用中的URL参数,支持各种复杂的数据结构。

主要特性

  • 支持基本数据类型(字符串、数字、布尔值)
  • 处理数组和嵌套结构
  • 自定义键名映射
  • 错误处理和验证
  • 零拷贝解析

安装方法

在Cargo.toml中添加依赖:

[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde-querystring = "0.2"

基本用法示例

1. 反序列化查询字符串

use serde::Deserialize;
use serde_querystring::from_str;

// 定义用户查询结构体
#[derive(Debug, Deserialize)]
struct UserQuery {
    name: String,
    age: u32,
    active: bool,
}

fn main() {
    let query = "name=John&age=25&active=true";
    let user: UserQuery = from_str(query).unwrap();
    
    println!("{:?}", user);
    // 输出: UserQuery { name: "John", age: 25, active: true }
}

2. 处理数组参数

// 定义搜索参数结构体
#[derive(Debug, Deserialize)]
struct SearchParams {
    q: String,
    tags: Vec<String>,
    page: u32,
}

fn main() {
    let query = "q=rust&tags=programming&tags=web&page=1";
    let params: SearchParams = from_str(query).unwrap();
    
    println!("搜索: {}", params.q);
    println!("标签: {:?}", params.tags);
    // 输出: 搜索: rust
    // 输出: 标签: ["programming", "web"]
}

3. 嵌套结构处理

// 定义过滤器结构体
#[derive(Debug, Deserialize)]
struct Filters {
    category: String,
    price_range: PriceRange,
}

// 定义价格范围结构体
#[derive(Debug, Deserialize)]
struct PriceRange {
    min: f32,
    max: f32,
}

fn main() {
    let query = "category=electronics&price_range[min]=100&price_range[max]=500";
    let filters: Filters = from_str(query).unwrap();
    
    println!("价格范围: {}-{}", filters.price_range.min, filters.price_range.max);
}

4. 序列化为查询字符串

use serde::Serialize;
use serde_querystring::to_string;

// 定义分页结构体
#[derive(Debug, Serialize)]
struct Pagination {
    page: u32,
    limit: u32,
    sort: String,
}

fn main() {
    let pagination = Pagination {
        page: 2,
        limit: 20,
        sort: "date".to_string(),
    };
    
    let query_string = to_string(&pagination).unwrap();
    println!("{}", query_string);
    // 输出: page=2&limit=20&sort=date
}

高级功能

自定义字段映射

use serde::Deserialize;

// 定义用户结构体,使用自定义字段映射
#[derive(Debug, Deserialize)]
struct User {
    #[serde(rename = "user_name")]
    name: String,
    
    #[serde(rename = "user_age")]
    age: u32,
}

fn main() {
    let query = "user_name=Alice&user_age=30";
    let user: User = from_str(query).unwrap();
}

可选字段处理

// 定义查询参数结构体,包含可选字段
#[derive(Debug, Deserialize)]
struct QueryParams {
    required: String,
    optional: Option<String>,
}

fn main() {
    let query = "required=value";
    let params: QueryParams = from_str(query).unwrap();
    
    println!("可选字段: {:?}", params.optional);
    // 输出: 可选字段: None
}

错误处理

// 错误处理函数示例
fn parse_query(query: &str) -> Result<UserQuery, serde_querystring::Error> {
    let result: Result<UserQuery, _> = from_str(query);
    
    match result {
        Ok(user) => Ok(user),
        Err(e) => {
            eprintln!("解析错误: {}", e);
            Err(e)
        }
    }
}

最佳实践

  1. 始终处理可能的解析错误
  2. 为可选字段使用Option类型
  3. 使用适当的错误类型进行验证
  4. 考虑查询字符串的长度限制

这个库为Rust开发者提供了强大而灵活的查询字符串处理能力,特别适合Web应用和API开发场景。

完整示例代码

use serde::{Deserialize, Serialize};
use serde_querystring::{from_str, to_string};

// 用户信息结构体
#[derive(Debug, Serialize, Deserialize)]
struct User {
    #[serde(rename = "user_id")]
    id: u32,
    
    #[serde(rename = "user_name")]
    name: String,
    
    #[serde(rename = "user_email")]
    email: Option<String>,
    
    roles: Vec<String>,
    is_active: bool,
}

// 分页参数结构体
#[derive(Debug, Serialize, Deserialize)]
struct Pagination {
    page: u32,
    per_page: u32,
    sort_by: Option<String>,
    order: Option<String>,
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 示例1: 反序列化复杂查询字符串
    let user_query = "user_id=123&user_name=John+Doe&roles=admin&roles=user&is_active=true";
    
    let user: User = from_str(user_query)?;
    println!("用户信息: {:?}", user);
    
    // 示例2: 序列化为查询字符串
    let pagination = Pagination {
        page: 1,
        per_page: 20,
        sort_by: Some("name".to_string()),
        order: Some("desc".to_string()),
    };
    
    let query_string = to_string(&pagination)?;
    println!("查询字符串: {}", query_string);
    
    // 示例3: 处理错误情况
    let invalid_query = "user_id=abc&user_name=John"; // user_id应该是数字
    
    match from_str::<User>(invalid_query) {
        Ok(user) => println!("成功解析: {:?}", user),
        Err(e) => println!("解析错误: {}", e),
    }
    
    Ok(())
}

// 错误处理包装函数
fn safe_parse<T: for<'de> Deserialize<'de>>(query: &str) -> Result<T, String> {
    from_str(query).map_err(|e| format!("解析失败: {}", e))
}

// 单元测试示例
#[cfg(test)]
mod tests {
    use super::*;
    
    #[test]
    fn test_basic_parsing() {
        let query = "user_id=456&user_name=Test&is_active=false";
        let user: User = from_str(query).unwrap();
        
        assert_eq!(user.id, 456);
        assert_eq!(user.name, "Test");
        assert_eq!(user.is_active, false);
    }
    
    #[test]
    fn test_array_parsing() {
        let query = "user_id=789&user_name=ArrayTest&roles=admin&roles=moderator&roles=user";
        let user: User = from_str(query).unwrap();
        
        assert_eq!(user.roles.len(), 3);
        assert!(user.roles.contains(&"admin".to_string()));
    }
}
回到顶部