rust - Rocket 测试 multipart/data-form 失败,出现 422(无法处理的实体)
问题描述
我想在这里测试我的上传路线。我有一个测试通过火箭测试客户端调用这条路线,但我总是得到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
我还使用附加参数运行 curl 并合并--trace-ascii -
请求,但我找不到任何差异导致测试期间出现 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);
}
}
货运.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"]
解决方案
我在 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);
}
}
推荐阅读
- python - Python遍历excel文件以查找信息
- ubuntu-16.04 - 如何打开没有显示的slimerjs?
- maven - Maven:如何将本地 lib 目录用于依赖项而不是 Maven 存储库
- php - 通过单击提交按钮提交表单数据时验证在 PHP 中不起作用
- python - Why groupby in Pandas print not all columns?
- c# - Object is being spammed when it comes to my timeBetweenShot variable, can anyone take a look?
- php - 如何测试本地环境和外部服务(webhook)之间的集成
- html - 如何使字体 FuturaPtBooks 在 html 中可用
- apache-kafka - Kafka 代理在清理日志文件时关闭
- git - 如何将作者从 svn 映射到 git?