分享一个tauri-plugin-http的工具类

使用tauri的时候在rust代码中发起http请求,分享一个tauri插件tauri-plugin-http的工具类:

同时分享一个自己写的小工具,使用tauri2+开发的粘贴板工具:https://jingchuanyuexiang.com

下面是工具类代码,复制即用:

#![allow(dead_code)]

use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use tauri_plugin_http::{
    reqwest,
    reqwest::header::{CONTENT_TYPE, HeaderMap, HeaderName, HeaderValue},
};

/// 统一API响应结构体
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ApiResponse<T> {
    pub code: i32,
    pub message: String,
    pub data: Option<T>,
    pub timestamp: i64,
}

/// 原始HTTP响应结构体(用于任意返回格式)
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RawResponse<T> {
    pub status: u16,
    pub headers: HashMap<String, String>,
    pub data: T,
    pub url: String,
}

/// HTTP请求配置
#[derive(Debug, Clone)]
pub struct HttpConfig {
    pub timeout: Option<u64>,
    pub headers: Option<HashMap<String, String>>,
    pub user_agent: Option<String>,
}

impl Default for HttpConfig {
    fn default() -> Self {
        Self {
            timeout: Some(30),
            headers: None,
            user_agent: Some("ClipPal/1.0".to_string()),
        }
    }
}

/// HTTP请求错误
#[derive(Debug, thiserror::Error)]
pub enum HttpError {
    #[error("请求失败: {0}")]
    RequestFailed(String),
    #[error("序列化失败: {0}")]
    SerializationFailed(String),
    #[error("反序列化失败: {0}")]
    DeserializationFailed(String),
    #[error("无效的URL: {0}")]
    InvalidUrl(String),
    #[error("超时: {0}")]
    Timeout(String),
    #[error("网络错误: {0}")]
    NetworkError(String),
    #[error("全局AppHandle获取失败")]
    AppHandleNotFound,
}

/// HTTP客户端
pub struct HttpClient {
    config: HttpConfig,
}

impl HttpClient {
    /// 创建新的HTTP客户端
    pub fn new() -> Self {
        Self {
            config: HttpConfig::default(),
        }
    }

    /// 使用自定义配置创建HTTP客户端
    pub fn with_config(config: HttpConfig) -> Self {
        Self { config }
    }

    /// 设置请求配置
    pub fn set_config(mut self, config: HttpConfig) -> Self {
        self.config = config;
        self
    }

    /// 设置超时时间
    pub fn timeout(mut self, timeout: u64) -> Self {
        self.config.timeout = Some(timeout);
        self
    }

    /// 设置请求头
    pub fn headers(mut self, headers: HashMap<String, String>) -> Self {
        self.config.headers = Some(headers);
        self
    }

    /// 设置User-Agent
    pub fn user_agent(mut self, user_agent: String) -> Self {
        self.config.user_agent = Some(user_agent);
        self
    }

    /// 发起GET请求(返回ApiResponse格式)
    pub async fn get<T>(&self, url: &str) -> Result<ApiResponse<T>, HttpError>
    where
        T: for<'de> Deserialize<'de>,
    {
        self.request::<(), T>("GET", url, None, None).await
    }

    /// 发起带查询参数的GET请求(返回ApiResponse格式)
    pub async fn get_with_params<T>(
        &self,
        url: &str,
        params: &HashMap<String, String>,
    ) -> Result<ApiResponse<T>, HttpError>
    where
        T: for<'de> Deserialize<'de>,
    {
        let url_with_params = self.build_url_with_params(url, params)?;
        self.request::<(), T>("GET", &url_with_params, None, None)
            .await
    }

    /// 发起POST请求(返回ApiResponse格式)
    pub async fn post<T, U>(&self, url: &str, data: Option<&T>) -> Result<ApiResponse<U>, HttpError>
    where
        T: Serialize,
        U: for<'de> Deserialize<'de>,
    {
        self.request("POST", url, data, None).await
    }

    /// 发起带JSON数据的POST请求(返回ApiResponse格式)
    pub async fn post_json<T, U>(&self, url: &str, data: &T) -> Result<ApiResponse<U>, HttpError>
    where
        T: Serialize,
        U: for<'de> Deserialize<'de>,
    {
        let mut headers = HashMap::new();
        headers.insert("Content-Type".to_string(), "application/json".to_string());
        self.request_with_headers("POST", url, Some(data), Some(headers))
            .await
    }

    /// 发起带表单数据的POST请求(返回ApiResponse格式)
    pub async fn post_form<T, U>(
        &self,
        url: &str,
        data: &HashMap<String, String>,
    ) -> Result<ApiResponse<U>, HttpError>
    where
        U: for<'de> Deserialize<'de>,
    {
        let mut headers = HashMap::new();
        headers.insert(
            "Content-Type".to_string(),
            "application/x-www-form-urlencoded".to_string(),
        );
        self.request_with_headers("POST", url, Some(data), Some(headers))
            .await
    }

    /// 发起带自定义请求头的请求(返回ApiResponse格式)
    pub async fn request_with_headers<T, U>(
        &self,
        method: &str,
        url: &str,
        data: Option<&T>,
        headers: Option<HashMap<String, String>>,
    ) -> Result<ApiResponse<U>, HttpError>
    where
        T: Serialize,
        U: for<'de> Deserialize<'de>,
    {
        let mut all_headers = self.config.headers.clone().unwrap_or_default();
        if let Some(custom_headers) = headers {
            all_headers.extend(custom_headers);
        }
        self.request(method, url, data, Some(all_headers)).await
    }

    // ========== 通用HTTP请求方法(返回任意格式) ==========

    /// 发起GET请求(返回原始响应格式)
    pub async fn get_raw<T>(&self, url: &str) -> Result<RawResponse<T>, HttpError>
    where
        T: for<'de> Deserialize<'de>,
    {
        self.request_raw::<(), T>("GET", url, None, None).await
    }

    /// 发起带查询参数的GET请求(返回原始响应格式)
    pub async fn get_with_params_raw<T>(
        &self,
        url: &str,
        params: &HashMap<String, String>,
    ) -> Result<RawResponse<T>, HttpError>
    where
        T: for<'de> Deserialize<'de>,
    {
        let url_with_params = self.build_url_with_params(url, params)?;
        self.request_raw::<(), T>("GET", &url_with_params, None, None)
            .await
    }

    /// 发起POST请求(返回原始响应格式)
    pub async fn post_raw<T, U>(
        &self,
        url: &str,
        data: Option<&T>,
    ) -> Result<RawResponse<U>, HttpError>
    where
        T: Serialize,
        U: for<'de> Deserialize<'de>,
    {
        self.request_raw("POST", url, data, None).await
    }

    /// 发起带JSON数据的POST请求(返回原始响应格式)
    pub async fn post_json_raw<T, U>(
        &self,
        url: &str,
        data: &T,
    ) -> Result<RawResponse<U>, HttpError>
    where
        T: Serialize,
        U: for<'de> Deserialize<'de>,
    {
        let mut headers = HashMap::new();
        headers.insert("Content-Type".to_string(), "application/json".to_string());
        self.request_with_headers_raw("POST", url, Some(data), Some(headers))
            .await
    }

    /// 发起带表单数据的POST请求(返回原始响应格式)
    pub async fn post_form_raw<T, U>(
        &self,
        url: &str,
        data: &HashMap<String, String>,
    ) -> Result<RawResponse<U>, HttpError>
    where
        U: for<'de> Deserialize<'de>,
    {
        let mut headers = HashMap::new();
        headers.insert(
            "Content-Type".to_string(),
            "application/x-www-form-urlencoded".to_string(),
        );
        self.request_with_headers_raw("POST", url, Some(data), Some(headers))
            .await
    }

    /// 发起带自定义请求头的请求(返回原始响应格式)
    pub async fn request_with_headers_raw<T, U>(
        &self,
        method: &str,
        url: &str,
        data: Option<&T>,
        headers: Option<HashMap<String, String>>,
    ) -> Result<RawResponse<U>, HttpError>
    where
        T: Serialize,
        U: for<'de> Deserialize<'de>,
    {
        let mut all_headers = self.config.headers.clone().unwrap_or_default();
        if let Some(custom_headers) = headers {
            all_headers.extend(custom_headers);
        }
        self.request_raw(method, url, data, Some(all_headers)).await
    }

    /// 核心请求方法(返回ApiResponse格式)
    async fn request<T, U>(
        &self,
        method: &str,
        url: &str,
        data: Option<&T>,
        headers: Option<HashMap<String, String>>,
    ) -> Result<ApiResponse<U>, HttpError>
    where
        T: Serialize,
        U: for<'de> Deserialize<'de>,
    {
        let response_text = self.execute_request(method, url, data, headers).await?;

        // 直接反序列化为ApiResponse<U>
        let api_response: ApiResponse<U> = serde_json::from_str(&response_text).map_err(|e| {
            HttpError::DeserializationFailed(format!("反序列化ApiResponse失败: {}", e))
        })?;

        Ok(api_response)
    }

    /// 核心请求方法(返回原始响应格式)
    async fn request_raw<T, U>(
        &self,
        method: &str,
        url: &str,
        data: Option<&T>,
        headers: Option<HashMap<String, String>>,
    ) -> Result<RawResponse<U>, HttpError>
    where
        T: Serialize,
        U: for<'de> Deserialize<'de>,
    {
        let (status, response_url, response_headers, response_text) = self
            .execute_request_with_response(method, url, data, headers)
            .await?;

        // 读取响应头
        let mut headers_map = HashMap::new();
        for (key, value) in response_headers.iter() {
            if let Ok(value_str) = value.to_str() {
                headers_map.insert(key.as_str().to_string(), value_str.to_string());
            }
        }

        // 反序列化响应数据
        let response_data = if response_text.is_empty() {
            serde_json::from_str("null").map_err(|e| {
                HttpError::DeserializationFailed(format!("反序列化空响应失败: {}", e))
            })?
        } else {
            serde_json::from_str(&response_text)
                .map_err(|e| HttpError::DeserializationFailed(format!("反序列化响应失败: {}", e)))?
        };

        Ok(RawResponse {
            status,
            headers: headers_map,
            data: response_data,
            url: response_url,
        })
    }

    /// 执行HTTP请求并返回响应文本(公共方法)
    async fn execute_request<T>(
        &self,
        method: &str,
        url: &str,
        data: Option<&T>,
        headers: Option<HashMap<String, String>>,
    ) -> Result<String, HttpError>
    where
        T: Serialize,
    {
        let (_, _, _, response_text) = self
            .execute_request_with_response(method, url, data, headers)
            .await?;
        Ok(response_text)
    }

    /// 执行HTTP请求并返回响应信息(公共方法)
    async fn execute_request_with_response<T>(
        &self,
        method: &str,
        url: &str,
        data: Option<&T>,
        headers: Option<HashMap<String, String>>,
    ) -> Result<(u16, String, reqwest::header::HeaderMap, String), HttpError>
    where
        T: Serialize,
    {
        // 打印请求信息(debug级别)
        log::debug!("=== HTTP请求开始 ===");
        log::debug!("请求方法: {}", method);
        log::debug!("请求URL: {}", url);
        
        // 验证URL
        let _parsed_url = reqwest::Url::parse(url)
            .map_err(|e| {
                let error_msg = format!("无效的URL: {}", e);
                log::error!("URL验证失败: {}", error_msg);
                HttpError::InvalidUrl(error_msg)
            })?;

        // 构建请求体
        let body = if let Some(data) = data {
            serde_json::to_string(data)
                .map_err(|e| {
                    let error_msg = format!("序列化请求数据失败: {}", e);
                    log::error!("请求数据序列化失败: {}", error_msg);
                    HttpError::SerializationFailed(error_msg)
                })?
        } else {
            String::new()
        };

        // 打印请求体信息(debug级别)
        if !body.is_empty() {
            log::debug!("请求体: {}", body);
        } else {
            log::debug!("请求体: 空");
        }

        // 构建请求头
        let mut header_map = HeaderMap::new();

        // 设置默认User-Agent
        if let Some(user_agent) = &self.config.user_agent {
            header_map.insert(
                "User-Agent",
                HeaderValue::from_str(user_agent)
                    .map_err(|e| {
                        let error_msg = format!("无效的User-Agent: {}", e);
                        log::error!("User-Agent设置失败: {}", error_msg);
                        HttpError::RequestFailed(error_msg)
                    })?,
            );
            log::debug!("User-Agent: {}", user_agent);
        }

        // 设置Content-Type
        if !body.is_empty() && method != "GET" {
            header_map.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
            log::debug!("Content-Type: application/json");
        }

        // 设置自定义请求头
        if let Some(custom_headers) = headers {
            log::debug!("自定义请求头:");
            for (key, value) in &custom_headers {
                let header_name = HeaderName::from_lowercase(key.to_lowercase().as_bytes())
                    .map_err(|e| {
                        let error_msg = format!("无效的请求头名称: {}", e);
                        log::error!("请求头名称无效: {}", error_msg);
                        HttpError::RequestFailed(error_msg)
                    })?;
                header_map.insert(
                    header_name,
                    HeaderValue::from_str(value)
                        .map_err(|e| {
                            let error_msg = format!("无效的请求头值: {}", e);
                            log::error!("请求头值无效: {}", error_msg);
                            HttpError::RequestFailed(error_msg)
                        })?,
                );
                log::debug!("  {}: {}", key, value);
            }
        }

        // 构建请求选项
        let mut options = tauri_plugin_http::reqwest::ClientBuilder::new();

        // 设置超时
        if let Some(timeout) = self.config.timeout {
            options = options.timeout(std::time::Duration::from_secs(timeout));
            log::debug!("请求超时设置: {}秒", timeout);
        }

        // 发起请求
        let client = options
            .build()
            .map_err(|e| {
                let error_msg = format!("创建HTTP客户端失败: {}", e);
                log::error!("HTTP客户端创建失败: {}", error_msg);
                HttpError::RequestFailed(error_msg)
            })?;

        let request_builder = match method.to_uppercase().as_str() {
            "GET" => client.get(url),
            "POST" => client.post(url).body(body),
            "PUT" => client.put(url).body(body),
            "DELETE" => client.delete(url),
            "PATCH" => client.patch(url).body(body),
            _ => {
                let error_msg = format!("不支持的HTTP方法: {}", method);
                log::error!("不支持的HTTP方法: {}", error_msg);
                return Err(HttpError::RequestFailed(error_msg));
            }
        };

        let request = request_builder
            .headers(header_map)
            .build()
            .map_err(|e| {
                let error_msg = format!("构建请求失败: {}", e);
                log::error!("请求构建失败: {}", error_msg);
                HttpError::RequestFailed(error_msg)
            })?;

        log::debug!("=== 开始执行HTTP请求 ===");

        // 执行请求
        let response = client
            .execute(request)
            .await
            .map_err(|e| {
                let error_msg = format!("网络请求失败: {}", e);
                log::error!("网络请求执行失败: {}", error_msg);
                log::error!("错误详情: {:?}", e);
                HttpError::NetworkError(error_msg)
            })?;

        let status = response.status().as_u16();
        let response_url = response.url().to_string();
        let response_headers = response.headers().clone();

        log::debug!("=== HTTP响应信息 ===");
        log::debug!("响应状态码: {}", status);
        log::debug!("响应URL: {}", response_url);
        log::debug!("响应头数量: {}", response_headers.len());

        // 打印响应头信息(debug级别)
        for (name, value) in response_headers.iter() {
            if let Ok(value_str) = value.to_str() {
                log::debug!("响应头 {}: {}", name, value_str);
            }
        }

        let response_text = response
            .text()
            .await
            .map_err(|e| {
                let error_msg = format!("读取响应失败: {}", e);
                log::error!("响应内容读取失败: {}", error_msg);
                log::error!("错误详情: {:?}", e);
                HttpError::NetworkError(error_msg)
            })?;

        // 根据状态码决定日志级别
        match status {
            200..=299 => {
                // 成功状态码 (2xx)
                log::debug!("=== HTTP请求成功 ===");
                log::debug!("状态码: {} (成功)", status);
                log::debug!("响应内容长度: {} 字符", response_text.len());
                // 如果响应内容太长,只打印前500个字符
                if response_text.len() > 500 {
                    log::debug!("响应内容预览: {}...", &response_text[..500]);
                } else {
                    log::debug!("响应内容: {}", response_text);
                }
            }
            300..=399 => {
                // 重定向状态码 (3xx)
                log::debug!("=== HTTP请求重定向 ===");
                log::debug!("状态码: {} (重定向)", status);
                log::debug!("响应内容: {}", response_text);
            }
            400..=499 => {
                // 客户端错误状态码 (4xx)
                log::error!("=== HTTP请求失败 (客户端错误) ===");
                log::error!("状态码: {} (客户端错误)", status);
                log::error!("响应内容: {}", response_text);
            }
            500..=599 => {
                // 服务器错误状态码 (5xx)
                log::error!("=== HTTP请求失败 (服务器错误) ===");
                log::error!("状态码: {} (服务器错误)", status);
                log::error!("响应内容: {}", response_text);
            }
            _ => {
                // 其他未知状态码
                log::warn!("=== HTTP请求未知状态 ===");
                log::warn!("状态码: {} (未知)", status);
                log::warn!("响应内容: {}", response_text);
            }
        }

        Ok((status, response_url, response_headers, response_text))
    }

    /// 构建带查询参数的URL
    fn build_url_with_params(
        &self,
        base_url: &str,
        params: &HashMap<String, String>,
    ) -> Result<String, HttpError> {
        let mut url = reqwest::Url::parse(base_url)
            .map_err(|e| HttpError::InvalidUrl(format!("无效的URL: {}", e)))?;

        for (key, value) in params {
            url.query_pairs_mut().append_pair(key, value);
        }

        Ok(url.to_string())
    }
}

/// 返回ApiResponse格式的HTTP请求函数
pub async fn get<T>(url: &str) -> Result<ApiResponse<T>, HttpError>
where
    T: for<'de> Deserialize<'de>,
{
    HttpClient::new().get(url).await
}

pub async fn post<T, U>(url: &str, data: &T) -> Result<ApiResponse<U>, HttpError>
where
    T: Serialize,
    U: for<'de> Deserialize<'de>,
{
    HttpClient::new().post_json(url, data).await
}

/// 便捷的HTTP请求函数(返回原始响应格式)
pub async fn get_raw<T>(url: &str) -> Result<RawResponse<T>, HttpError>
where
    T: for<'de> Deserialize<'de>,
{
    HttpClient::new().get_raw(url).await
}

pub async fn post_raw<T, U>(url: &str, data: &T) -> Result<RawResponse<U>, HttpError>
where
    T: Serialize,
    U: for<'de> Deserialize<'de>,
{
    HttpClient::new().post_json_raw(url, data).await
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值