Rust HTTP请求中间件库reqwest-conditional-middleware的使用,实现条件性请求处理和响应缓存控制

Rust HTTP请求中间件库reqwest-conditional-middleware的使用,实现条件性请求处理和响应缓存控制

安装

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

cargo add reqwest-conditional-middleware

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

reqwest-conditional-middleware = "0.4.0"

完整示例代码

以下是一个使用reqwest-conditional-middleware实现条件性请求处理和响应缓存控制的完整示例:

use reqwest::{Client, Request, Response};
use reqwest_conditional_middleware::ConditionalMiddleware;
use std::time::{SystemTime, UNIX_EPOCH};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 创建带有条件中间件的客户端
    let client = Client::builder()
        .with(ConditionalMiddleware::default())
        .build()?;
    
    // 示例URL
    let url = "https://example.com/api/data";
    
    // 构建请求
    let request = client.get(url)
        // 添加If-None-Match条件
        .header("If-None-Match", "\"some-etag\"")
        // 添加If-Modified-Since条件
        .header("If-Modified-Since", "Wed, 21 Oct 2015 07:28:00 GMT")
        .build()?;
    
    // 发送请求
    let response = client.execute(request).await?;
    
    // 处理响应
    if response.status() == 304 {
        println!("资源未修改,可以使用缓存版本");
    } else {
        println!("获取到新资源");
        // 处理响应数据...
        if let Some(etag) = response.headers().get("ETag") {
            println!("ETag: {:?}", etag);
        }
        if let Some(last_modified) = response.headers().get("Last-Modified") {
            println!("Last-Modified: {:?}", last_modified);
        }
    }
    
    Ok(())
}

功能说明

  1. 条件性请求处理

    • 通过添加If-None-MatchIf-Modified-Since等请求头,服务器可以返回304状态码表示资源未修改
    • 这可以减少不必要的数据传输,提高性能
  2. 响应缓存控制

    • 中间件会自动处理ETagLast-Modified等响应头
    • 可以基于这些信息构建后续的条件请求
  3. 缓存验证

    • 当服务器返回304状态码时,表示客户端的缓存仍然有效
    • 可以避免重新下载未修改的资源

注意事项

  1. 该中间件需要与reqwest库一起使用
  2. 确保正确设置请求的条件头字段
  3. 处理304响应时需要从本地缓存获取数据
  4. 对于POST等非安全/非幂等方法,条件请求可能不适用

这个示例展示了如何使用reqwest-conditional-middleware来优化HTTP请求,减少不必要的网络传输,提高应用程序性能。

更完整的示例代码

以下是一个更完整的示例,包含本地缓存处理和自动条件头设置:

use reqwest::{Client, Request, Response};
use reqwest_conditional_middleware::ConditionalMiddleware;
use std::collections::HashMap;
use std::sync::Mutex;
use std::time::{SystemTime, UNIX_EPOCH};

// 简单的内存缓存结构
struct Cache {
    data: Mutex<HashMap<String, (String, String)>>,
}

impl Cache {
    fn new() -> Self {
        Self {
            data: Mutex::new(HashMap::new()),
        }
    }

    fn get(&self, url: &str) -> Option<(String, String)> {
        self.data.lock().unwrap().get(url).cloned()
    }

    fn set(&self, url: String, etag: String, last_modified: String) {
        self.data.lock().unwrap().insert(url, (etag, last_modified));
    }
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 创建缓存实例
    let cache = Cache::new();
    
    // 创建带有条件中间件的客户端
    let client = Client::builder()
        .with(ConditionalMiddleware::default())
        .build()?;
    
    // 示例URL
    let url = "https://example.com/api/data";
    
    // 检查缓存
    let mut request_builder = client.get(url);
    
    if let Some((etag, last_modified)) = cache.get(url) {
        // 如果缓存中存在,添加条件头
        request_builder = request_builder
            .header("If-None-Match", format!("\"{}\"", etag))
            .header("If-Modified-Since", last_modified);
    }
    
    // 构建请求
    let request = request_builder.build()?;
    
    // 发送请求
    let response = client.execute(request).await?;
    
    // 处理响应
    match response.status() {
        reqwest::StatusCode::NOT_MODIFIED => {
            println!("资源未修改,使用缓存版本");
            // 从缓存中获取数据...
        }
        _ => {
            println!("获取到新资源");
            // 处理新数据...
            
            // 更新缓存
            if let (Some(etag), Some(last_modified)) = (
                response.headers().get("ETag"),
                response.headers().get("Last-Modified"),
            ) {
                cache.set(
                    url.to_string(),
                    etag.to_str()?.to_string(),
                    last_modified.to_str()?.to_string(),
                );
            }
        }
    }
    
    Ok(())
}

这个更完整的示例添加了以下功能:

  1. 简单的内存缓存实现
  2. 自动从缓存中读取ETag和Last-Modified值
  3. 自动为后续请求设置条件头
  4. 根据响应状态码处理缓存逻辑
  5. 更新缓存中的ETag和Last-Modified值

1 回复

以下是基于您提供的内容整理的完整示例demo,包含基本使用和高级配置的完整代码示例:

基本使用方法示例

use reqwest_conditional_middleware::{ConditionalMiddleware, ConditionalMiddlewareBuilder};
use reqwest::Client;
use std::time::Duration;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 创建带有默认配置的条件请求中间件的客户端
    let client = Client::builder()
        .build()?
        .with(ConditionalMiddleware::default());
    
    // 发送GET请求
    let response = client.get("https://api.example.com/data")
        .send()
        .await?;
    
    // 打印响应状态和头信息
    println!("状态码: {}", response.status());
    println!("响应头: {:#?}", response.headers());
    
    // 如果响应成功(200 OK),读取响应体
    if response.status().is_success() {
        let body = response.text().await?;
        println!("响应体: {}", body);
    }
    
    Ok(())
}

高级配置完整示例

use reqwest_conditional_middleware::{ConditionalMiddlewareBuilder, CacheBackend, CacheEntry};
use reqwest::Client;
use std::sync::Arc;
use std::time::Duration;
use async_trait::async_trait;

// 自定义内存缓存后端实现
struct InMemoryCacheBackend {
    cache: std::sync::Mutex<std::collections::HashMap<String, CacheEntry>>,
}

impl InMemoryCacheBackend {
    fn new() -> Self {
        Self {
            cache: std::sync::Mutex::new(std::collections::HashMap::new()),
        }
    }
}

#[async_trait]
impl CacheBackend for InMemoryCacheBackend {
    async fn get(&self, key: &str) -> Option<CacheEntry> {
        // 从内存HashMap中获取缓存
        self.cache.lock().unwrap().get(key).cloned()
    }
    
    async fn set(&self, key: &str, entry: CacheEntry) {
        // 将缓存存入内存HashMap
        self.cache.lock().unwrap().insert(key.to_string(), entry);
    }
    
    async fn delete(&self, key: &str) {
        // 从内存HashMap中删除缓存
        self.cache.lock().unwrap().remove(key);
    }
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 创建自定义缓存后端
    let cache_backend = Arc::new(InMemoryCacheBackend::new());
    
    // 使用构建器配置中间件
    let middleware = ConditionalMiddlewareBuilder::new()
        .cache_backend(cache_backend) // 设置自定义缓存后端
        .default_max_age(Duration::from_secs(300)) // 默认缓存5分钟
        .force_refresh(false) // 不强制刷新缓存
        .build();
    
    // 创建带有自定义中间件的客户端
    let client = Client::builder()
        .build()?
        .with(middleware);
    
    // 发送条件性GET请求
    let response = client.get("https://api.example.com/conditional-data")
        .send()
        .await?;
    
    match response.status() {
        // 成功响应(200 OK)
        status if status.is_success() => {
            let text = response.text().await?;
            println!("获取到新数据: {}", text);
        },
        // 未修改(304 Not Modified)
        status if status == 304 => {
            println!("数据未修改,使用缓存版本");
        },
        // 其他状态码
        _ => {
            eprintln!("请求失败: {}", response.status());
        }
    }
    
    Ok(())
}

示例说明

  1. 基本使用示例展示了如何创建一个带有默认配置的条件请求中间件的reqwest客户端,并发送一个简单的GET请求。

  2. 高级配置示例展示了:

    • 如何实现一个简单的内存缓存后端(InMemoryCacheBackend)
    • 如何使用ConditionalMiddlewareBuilder自定义中间件配置
    • 如何处理不同的响应状态码(200 OK和304 Not Modified)
  3. 两个示例都使用了tokio运行时,因为reqwestreqwest-conditional-middleware都是异步的。

  4. 在实际应用中,您可能需要根据需求调整缓存时间、缓存后端实现等参数。

这个库确实可以显著提高应用程序的网络效率,特别是在需要频繁请求相同API端点或处理大型资源时。

回到顶部