如何使用带有 Rocket 的 abonander/multipart 解析多部分表单?

Posted

技术标签:

【中文标题】如何使用带有 Rocket 的 abonander/multipart 解析多部分表单?【英文标题】:How to parse multipart forms using abonander/multipart with Rocket? 【发布时间】:2017-09-11 12:10:16 【问题描述】:

This might be useful for me:

我不知道您打算如何解析多部分表单 除了使用原始的后数据字符串作为输入手动进行之外

我会尝试调整the Hyper example,但我们将不胜感激。

相关问题:

Support Multipart Forms。 support rocket

【问题讨论】:

生成minimal reproducible example 将大大有助于获得好的答案。就像现在一样,这个问题感觉很像“请为我编写代码”。提问时应为show a large amount of effort。现在,除了实际解决问题之外,任何回答者都必须从头开始创建一个基本的 Rocket 应用程序以及一个适用的客户端来测试它。显示的工作量和要求的工作量存在巨大差异。 我正计划在了解更多信息后扩展我的问题,但您的回答太快了 :) 谢谢 【参考方案1】:

Rocket 对数据的主要抽象是 FromData 特征。给定 POST 数据和请求,您可以构造一个给定的类型:

pub trait FromData<'a>: Sized 
    type Error;
    type Owned: Borrow<Self::Borrowed>;
    type Borrowed: ?Sized;
    fn transform(
        request: &Request, 
        data: Data
    ) -> Transform<Outcome<Self::Owned, Self::Error>>;
    fn from_data(
        request: &Request, 
        outcome: Transformed<'a, Self>
    ) -> Outcome<Self, Self::Error>;

然后,只需阅读API for multipart 并将标签 A 插入插槽 B:

#![feature(proc_macro_hygiene, decl_macro)]

use multipart::server::Multipart; // 0.16.1, default-features = false, features = ["server"]
use rocket::
    data::Data, FromData, Outcome, Transform, Transformed,
    post, routes, Request,
; // 0.4.2
use std::io::Read;

#[post("/", data = "<upload>")]
fn index(upload: DummyMultipart) -> String 
    format!("I read this: :?", upload)


#[derive(Debug)]
struct DummyMultipart 
    alpha: String,
    one: i32,
    file: Vec<u8>,


// All of the errors in these functions should be reported
impl<'a> FromData<'a> for DummyMultipart 
    type Owned = Vec<u8>;
    type Borrowed = [u8];
    type Error = ();

    fn transform(_request: &Request, data: Data) -> Transform<Outcome<Self::Owned, Self::Error>> 
        let mut d = Vec::new();
        data.stream_to(&mut d).expect("Unable to read");

        Transform::Owned(Outcome::Success(d))
    

    fn from_data(request: &Request, outcome: Transformed<'a, Self>) -> Outcome<Self, Self::Error> 
        let d = outcome.owned()?;

        let ct = request
            .headers()
            .get_one("Content-Type")
            .expect("no content-type");
        let idx = ct.find("boundary=").expect("no boundary");
        let boundary = &ct[(idx + "boundary=".len())..];

        let mut mp = Multipart::with_body(&d[..], boundary);

        // Custom implementation parts

        let mut alpha = None;
        let mut one = None;
        let mut file = None;

        mp.foreach_entry(|mut entry| match &*entry.headers.name 
            "alpha" => 
                let mut t = String::new();
                entry.data.read_to_string(&mut t).expect("not text");
                alpha = Some(t);
            
            "one" => 
                let mut t = String::new();
                entry.data.read_to_string(&mut t).expect("not text");
                let n = t.parse().expect("not number");
                one = Some(n);
            
            "file" => 
                let mut d = Vec::new();
                entry.data.read_to_end(&mut d).expect("not file");
                file = Some(d);
            
            other => panic!("No known key ", other),
        )
        .expect("Unable to iterate");

        let v = DummyMultipart 
            alpha: alpha.expect("alpha not set"),
            one: one.expect("one not set"),
            file: file.expect("file not set"),
        ;

        // End custom

        Outcome::Success(v)
    


fn main() 
    rocket::ignite().mount("/", routes![index]).launch();

我从未真正使用过这些 API,因此无法保证这是一个好的实现。事实上,所有对错误的恐慌肯定意味着它不是最理想的。生产使用会干净地处理所有这些。

但是,它确实有效:

%curl -X POST -F alpha=omega -F one=2 -F file=@hello http://localhost:8000/
I read this: DummyMultipart  alpha: "omega", one: 2, file: [104, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100, 33, 10] 

高级实现将允许在用户特定数据和通用多部分方面之间进行一些抽象。像Multipart&lt;MyForm&gt; 这样的东西会很好。

Rocketpoints out 的作者表示,此解决方案允许恶意最终用户发布无限大小的文件,这将导致机器内存不足。根据预期用途,您可能希望对读取的字节数设置某种上限,可能会在某个断点处写入文件系统。

【讨论】:

【参考方案2】:

Rocket is still being discussed 官方支持多部分表单解析。在那之前,看一下如何将 multipart crate 与 Rocket 集成的官方示例:https://github.com/abonander/multipart/blob/master/examples/rocket.rs

【讨论】:

Mods:链接到外部代码而不是将其集成到答案中通常是不好的做法,但在这种情况下,代码可能会随着火箭和多部分库一起更改,因此没有意义将近 100 行代码复制粘贴到我的答案中。 “我如何将这个库与火箭一起使用”的答案是“看看这个官方的最新示例”。 @Dharman 感谢您的注意 :) 我更新了答案,您认为现在更好了吗?

以上是关于如何使用带有 Rocket 的 abonander/multipart 解析多部分表单?的主要内容,如果未能解决你的问题,请参考以下文章

如何在生产中使用 Rocket 运行 Diesel 迁移?

Rust 的 Rocket Web 框架如何使用共享变量?

Rocket.Chat 传入 Webhook:以特定响应回答(用于 facebook 集成)

Rocket CORS 如何使用 Request Guard 返回字符串?

带有 eppn 的 REST-API users.list

PSP(Rocket Software 产品)如何提高 Mainframe 作业的性能