编程 Rust API 服务器:发送和接收字节数据

2024-11-19 05:47:24 +0800 CST views 516

准备工作

在现代网络应用中,处理二进制数据(如文件上传和下载)是不可避免的任务。本文将深入探讨如何使用 Rust 构建强大的 API 服务器,以处理发送和接收字节数据的各种场景。我们将使用 axum 框架,这是一个非常适合路由请求和声明式解析请求的 Web 应用框架。

首先,在 Cargo.toml 文件中添加必要的依赖项:

[dependencies]
tokio = { version = "1", features = ["macros"] }
lambda_http = "0.13.0"
axum = { version = "0.7.5", features = ["multipart"] }
tower-http = { version = "0.5.2", features = ["limit"] }
regex = "1.10.6"
urlencoding = "2.1.3"

这里我们添加了 axum 用于构建 Web 应用程序,tokio 用于异步运行时,tower-http 用于设置请求体大小限制,regex 用于解析文件名,urlencoding 用于处理文件名编码,以及 lambda_http 用于在 AWS Lambda 上部署(可选)。

接收二进制数据

直接接收字节数据

当直接接收请求体中的字节数据时,需要在处理函数中完成以下步骤:

  1. Content-Disposition 头部信息中提取 file_name
  2. Bytes 类型转换为 Vec<u8> 类型,以便后续处理,例如保存文件。
use axum::{http::HeaderMap, Json, extract::Bytes, response::IntoResponse};
use serde_json::{json, Value};
use regex::Regex;
use urlencoding::decode;

async fn post_bytes(headers: HeaderMap, bytes: Bytes) -> Json<Value> {
    let Some(content_disposition) = headers.get("content-disposition") else {
        return Json(json!({"message": "content disposition not available"}));
    };

    let Ok(content_disposition_string) = content_disposition.to_str() else {
        return Json(json!({"message": "content disposition not available"}));
    };

    let Ok(re) = Regex::new(r#"((.|\\s\\S|\\r|\\n)*)filename\*?=utf-8''(?<name>((.|\\s\\S|\\r|\\n)*))"#) else {
        return Json(json!({"message": "Regex failed to create."}));
    };

    let Some(captures) = re.captures(content_disposition_string) else {
        return Json(json!({"message": "File name not found."}));
    };

    let file_name = decode(&captures["name"]).expect("UTF-8").to_string();
    let data: Vec<u8> = match bytes.bytes().collect() {
        Ok(data) => data,
        Err(err) => {
            return Json(json!({"message": format!("Bytes data not available. Error: {}", err.to_string())}));
        }
    };

    Json(json!({"file_name": file_name, "data_length": data.len()}))
}

处理 Multipart/form-data 数据

对于 Multipart/form-data 类型的请求,我们使用 axum::extract::Multipart 来处理。

use axum::extract::Multipart;

async fn post_bytes_multiparts(mut multipart: Multipart) -> Json<Value> {
    while let Some(field) = multipart.next_field().await.unwrap() {
        let name = field.name().unwrap().to_string();
        let file_name = field.file_name().unwrap().to_string();
        let content_type = field.content_type().unwrap().to_string();
        let data = field.bytes().await.unwrap();

        if name == "file" {
            return Json(json!({"file_name": file_name, "content_type": content_type, "data_length": data.len()}));
        }
    }

    Json(json!({"message": "file field is missing in the form."}))
}

设置请求体大小限制

为了防止恶意请求导致服务器资源耗尽,可以设置请求体大小限制。默认情况下,axum 会将请求体大小限制为 2MB。

use axum::body::Body;
use axum::http::Request;
use tower::ServiceBuilder;
use tower_http::limit::RequestBodyLimitLayer;
use std::convert::Infallible;

let app = Router::new()
    .layer(
        ServiceBuilder::new()
            .layer(RequestBodyLimitLayer::disable())
            .layer(RequestBodyLimitLayer::new(10 * 1024 * 1024 * 1024)),
    );

发送二进制数据

发送二进制数据时,我们需要确保响应头中的 Content-TypeContent-Disposition 设置正确。

use axum::http::{StatusCode, HeaderValue};
use urlencoding::encode;

async fn echo_bytes(headers: HeaderMap, bytes: Bytes) -> Response {
    let Some(content_type) = headers.get("content-type") else {
        return build_error_response("content type not available");
    };

    let Some(content_disposition) = headers.get("content-disposition") else {
        return build_error_response("content disposition not available");
    };

    let Ok(content_disposition_string) = content_disposition.to_str() else {
        return build_error_response("content disposition not available");
    };

    let Ok(re) = Regex::new(r#"((.|\\s\\S|\\r|\\n)*)filename\*?=utf-8''(?<name>((.|\\s\\S|\\r|\\n)*))"#) else {
        return build_error_response("Regex failed to create");
    };

    let Some(captures) = re.captures(content_disposition_string) else {
        return build_error_response("File name not found in Content-Disposition");
    };

    let file_name = decode(&captures["name"]).expect("UTF-8").to_string();
    let data: Vec<u8> = match bytes.bytes().collect() {
        Ok(data) => data,
        Err(err) => {
            return build_error_response(&format!("Bytes data not available. Error: {}", err.to_string()));
        }
    };

    let file_name_encode = encode(&file_name).to_string();
    let mut headers = HeaderMap::new();
    headers.insert("content-type", content_type.to_owned());
    headers.insert("content-length", HeaderValue::from(bytes.len()));
    headers.insert("content-disposition", HeaderValue::from_str(&format!("attachment; filename*=utf-8''{}", file_name_encode)).unwrap());

    (headers, data).into_response()
}

总结

本文详细介绍了如何使用 Rust 构建 API 服务器来处理发送和接收字节数据。我们学习了如何使用 axum 框架处理不同类型的请求,如何设置请求体大小限制,以及如何正确设置响应头以发送二进制数据。


复制全文 生成海报 编程 Web开发 Rust API 数据处理

推荐文章

liunx宝塔php7.3安装mongodb扩展
2024-11-17 11:56:14 +0800 CST
LLM驱动的强大网络爬虫工具
2024-11-19 07:37:07 +0800 CST
php获取当前域名
2024-11-18 00:12:48 +0800 CST
Linux查看系统配置常用命令
2024-11-17 18:20:42 +0800 CST
Rust 中的所有权机制
2024-11-18 20:54:50 +0800 CST
如何实现生产环境代码加密
2024-11-18 14:19:35 +0800 CST
Golang实现的交互Shell
2024-11-19 04:05:20 +0800 CST
Vue 3 是如何实现更好的性能的?
2024-11-19 09:06:25 +0800 CST
markdowns滚动事件
2024-11-19 10:07:32 +0800 CST
网站日志分析脚本
2024-11-19 03:48:35 +0800 CST
Golang在整洁架构中优雅使用事务
2024-11-18 19:26:04 +0800 CST
Vue3中如何实现插件?
2024-11-18 04:27:04 +0800 CST
Golang 几种使用 Channel 的错误姿势
2024-11-19 01:42:18 +0800 CST
为什么要放弃UUID作为MySQL主键?
2024-11-18 23:33:07 +0800 CST
初学者的 Rust Web 开发指南
2024-11-18 10:51:35 +0800 CST
git使用笔记
2024-11-18 18:17:44 +0800 CST
JavaScript 上传文件的几种方式
2024-11-18 21:11:59 +0800 CST
最全面的 `history` 命令指南
2024-11-18 21:32:45 +0800 CST
15 个 JavaScript 性能优化技巧
2024-11-19 07:52:10 +0800 CST
filecmp,一个Python中非常有用的库
2024-11-19 03:23:11 +0800 CST
程序员茄子在线接单