【问题标题】:Rocket testing multipart/data-form fails with 422 (Unprocessable Entity)Rocket 测试 multipart/data-form 失败,出现 422(无法处理的实体)
【发布时间】:2021-06-29 11:09:51
【问题描述】:

我想在这里测试我的上传路线。我有一个测试通过火箭测试客户端调用这条路线,但我总是得到Status { code: 422, reason: "Unprocessable Entity" } 作为响应。

但是,我无法弄清楚我的请求正文有什么问题。

另外,如果有另一种方法来测试 multipart/data-form,真的很受欢迎。

测试失败但运行应用程序时我可以成功调用上传路由,我的curl命令是:curl -X POST -H "Accept: application/json" -F file=@/home/username/Downloads/example.jpg -F id="1588a509-3517-49f2-9dea-1791c2e99db9" -F allowed_file_types="image/jpeg" http://localhost:8000/upload

我还使用附加参数 --trace-ascii - 运行 curl 并合并请求,但我找不到任何差异导致测试期间出现 422 响应,但在使用 curl 时有效。

main.rs

#[macro_use] extern crate rocket;

use rocket::data::TempFile;
use rocket::form::Form;
use rocket_contrib::json::{json, JsonValue};
use rocket_contrib::uuid::Uuid;

#[derive(FromForm)]
pub struct FileUploadForm<'f> {
    id: Uuid,
    #[field(validate = len(1..))]
    allowed_file_types: Vec<String>,
    file: TempFile<'f>,
}

#[post("/upload", data = "<form>")]
pub async fn upload(mut form: Form<FileUploadForm<'_>>) -> JsonValue {
    json!({"status": "ok"})
}

fn rocket() -> rocket::Rocket {
    let rocket = rocket::ignite();

    rocket
        .mount("/", routes![upload])
}

#[rocket::main]
async fn main() {
    rocket()
        .launch()
        .await;
}

#[cfg(test)]
mod tests {
    extern crate image;

    use super::*;

    use std::str;
    use rocket::local::blocking::Client;
    use rocket::http::{Status, Header};
    use std::{env, io};
    use std::fs::File;
    use std::io::{Write, Read};
    use std::path::PathBuf;
    use self::image::RgbImage;


    fn create_image(file_name: &str) -> PathBuf {
        let path_buf = env::temp_dir().join(file_name);

        let imgbuf: RgbImage= image::ImageBuffer::new(256, 256);
        imgbuf.save(path_buf.as_path()).unwrap();

        path_buf
    }

    fn image_data(boundary: &str) -> io::Result<Vec<u8>> {
        // https://stackoverflow.com/questions/51397872/how-to-post-an-image-using-multipart-form-data-with-hyper
        // https://golangbyexample.com/multipart-form-data-content-type-golang/
        let mut data = Vec::new();

        // start
        write!(data, "--{}\r\n", boundary)?;

        // image data
        write!(data, "Content-Disposition: form-data; name=\"file\"; filename=\"image.jpg\"\r\n")?;
        write!(data, "Content-Type: image/jpeg\r\n")?;
        write!(data, "\r\n")?;

        let path_buf = create_image("image.jpg");
        let mut f = File::open(path_buf.as_path())?;
        f.read_to_end(&mut data)?;
        write!(data, "\r\n")?;
        
        // id
        write!(data, "--{}\r\n", boundary)?;
        write!(data, "Content-Disposition: form-data; name=\"id\"\r\n")?;
        write!(data, "\r\n")?;

        write!(data, "1588a509-3517-49f2-9dea-1791c2e99db9")?;

        // allowed_file_types
        write!(data, "--{}\r\n", boundary)?;
        write!(data, "Content-Disposition: form-data; name=\"allowed_file_types\"\r\n")?;
        write!(data, "\r\n")?;

        write!(data, "image/jpeg")?;
        write!(data, "--{}\r\n", boundary)?;

        // end
        write!(data, "--{}--\r\n", boundary)?;

        Ok(data)
    }

    #[test]
    fn upload_file() {
        const BOUNDARY: &str = "--------------------------------XYZ";

        let client = Client::tracked(rocket()).expect("valid rocket instance");
        let accept = Header::new("Accept", "application/json");
        let content_type = Header::new("Content-Type", format!("multipart/form-data; boundary={}", BOUNDARY));

        let response = client
            .post("/upload")
            .header(content_type)
            .header(accept)
            .body(image_data(BOUNDARY).unwrap())
            .dispatch();

        assert_eq!(response.status(), Status::Ok);
    }
}

Cargo.toml

[package]
name = "example"
version = "0.1.0"
edition = "2018"

[dependencies]
rocket = { git = "https://github.com/SergioBenitez/Rocket", version = "0.5.0-dev" }
image = { version = "^0"}

[dependencies.rocket_contrib]
git = "https://github.com/SergioBenitez/Rocket"
version = "0.5.0-dev"
default-features = false
features = ["json", "uuid"]

【问题讨论】:

    标签: rust rust-rocket


    【解决方案1】:

    我在 id 部分之后缺少了一个新行,正确的代码如下。我已经评论了导致错误的一行以及我添加了不必要的边界的另一行。

    #[macro_use] extern crate rocket;
    
    use rocket::data::TempFile;
    use rocket::form::Form;
    use rocket_contrib::json::{json, JsonValue};
    use rocket_contrib::uuid::Uuid;
    
    #[derive(FromForm)]
    pub struct FileUploadForm<'f> {
        id: Uuid,
        #[field(validate = len(1..))]
        allowed_file_types: Vec<String>,
        file: TempFile<'f>,
    }
    
    #[post("/upload", data = "<form>")]
    pub async fn upload(mut form: Form<FileUploadForm<'_>>) -> JsonValue {
        json!({"status": "ok"})
    }
    
    fn rocket() -> rocket::Rocket {
        let rocket = rocket::ignite();
    
        rocket
            .mount("/", routes![upload])
    }
    
    #[rocket::main]
    async fn main() {
        rocket()
            .launch()
            .await;
    }
    
    #[cfg(test)]
    mod tests {
        extern crate image;
    
        use super::*;
    
        use std::str;
        use rocket::local::blocking::Client;
        use rocket::http::{Status, Header};
        use std::{env, io};
        use std::fs::File;
        use std::io::{Write, Read};
        use std::path::PathBuf;
        use self::image::RgbImage;
    
    
        fn create_image(file_name: &str) -> PathBuf {
            let path_buf = env::temp_dir().join(file_name);
    
            let imgbuf: RgbImage= image::ImageBuffer::new(256, 256);
            imgbuf.save(path_buf.as_path()).unwrap();
    
            path_buf
        }
    
        fn image_data(boundary: &str) -> io::Result<Vec<u8>> {
            // https://stackoverflow.com/questions/51397872/how-to-post-an-image-using-multipart-form-data-with-hyper
            // https://golangbyexample.com/multipart-form-data-content-type-golang/
            let mut data = Vec::new();
    
            // start
            write!(data, "--{}\r\n", boundary)?;
    
            // image data
            write!(data, "Content-Disposition: form-data; name=\"file\"; filename=\"image.jpg\"\r\n")?;
            write!(data, "Content-Type: image/jpeg\r\n")?;
            write!(data, "\r\n")?;
    
            let path_buf = create_image("image.jpg");
            let mut f = File::open(path_buf.as_path())?;
            f.read_to_end(&mut data)?;
            write!(data, "\r\n")?;
    
            write!(data, "--{}\r\n", boundary)?;
            
            // id
            write!(data, "Content-Disposition: form-data; name=\"id\"\r\n")?;
            write!(data, "\r\n")?;
    
            write!(data, "1588a509-3517-49f2-9dea-1791c2e99db9")?;
            write!(data, "\r\n")?;  // I was missing this
    
            // allowed_file_types
            write!(data, "--{}\r\n", boundary)?;
            write!(data, "Content-Disposition: form-data; name=\"allowed_file_types\"\r\n")?;
            write!(data, "\r\n")?;
    
            write!(data, "image/jpeg")?;
            write!(data, "\r\n")?;  // No boundary needed here, however did not cause an error
    
            // end
            write!(data, "--{}--\r\n", boundary)?;
    
            Ok(data)
        }
    
        #[test]
        fn upload_file() {
            const BOUNDARY: &str = "--------------------------------XYZ";
    
            let client = Client::tracked(rocket()).expect("valid rocket instance");
            let accept = Header::new("Accept", "application/json");
            let content_type = Header::new("Content-Type", format!("multipart/form-data; boundary={}", BOUNDARY));
    
            let response = client
                .post("/upload")
                .header(content_type)
                .header(accept)
                .body(image_data(BOUNDARY).unwrap())
                .dispatch();
    
            assert_eq!(response.status(), Status::Ok);
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-05-27
      • 1970-01-01
      • 1970-01-01
      • 2012-09-17
      • 1970-01-01
      相关资源
      最近更新 更多