By Guest
Unnamed Paste
Auto Detect
6.94 KiB
17
14 days ago
/// Representing a file in a multipart form.
/// This must be used with [`Multipart`]
#[derive(Debug, Deserialize)]
pub struct File {
pub content_type: String,
pub name: String,
pub bytes: Vec<u8>,
}
/// Extractor to extract multipart forms from the request
pub struct Multipart<T> {
data: T,
}
impl<T> Multipart<T> {
fn new(data: T) -> Self {
Multipart::<T> { data }
}
}
impl<T> Deref for Multipart<T> {
type Target = T;
fn deref(&self) -> &T {
&self.data
}
}
impl<T: serde::de::DeserializeOwned> FromRequest for Multipart<T> {
type Error = Error;
type Future = Pin<Box<dyn Future<Output = Result<Self, Self::Error>>>>;
fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future {
let mut multipart = actix_multipart::Multipart::new(req.headers(), payload.take());
let existing_fields = serde_introspect::<T>();
let req_owned = req.to_owned();
Box::pin(async move {
let mut params = Map::new();
while let Ok(Some(mut field)) = multipart.try_next().await {
let disposition = field.content_disposition().clone();
let field_name = disposition.get_name().unwrap();
let field_name_formatted = field_name.replace("[]", "");
// Make sure the field actually exists on the form
if !existing_fields.contains(&field_name) {
continue;
}
if field.content_disposition().get_filename().is_some() {
// Is a file
let mut data: Vec<Value> = Vec::new();
while let Some(chunk) = field.next().await {
match chunk {
Ok(bytes) => {
let chunk_data = bytes.to_vec();
data.reserve_exact(chunk_data.len());
for byte in chunk_data {
data.push(Value::Number(Number::from(byte)));
}
}
Err(_) => {
params.insert(field_name_formatted.to_owned(), Value::Null);
continue;
}
}
}
let mut field_map = Map::new();
field_map.insert(
"content_type".to_owned(),
Value::String(field.content_type().to_string()),
);
field_map.insert(
"name".to_owned(),
Value::String(
field
.content_disposition()
.get_filename()
.unwrap()
.to_string(),
),
);
field_map.insert("bytes".to_owned(), Value::Array(data));
params_insert(
&mut params,
field_name,
&field_name_formatted,
Value::Object(field_map),
);
} else if let Some(Ok(value)) = field.next().await {
// Not a file, parse as other JSON types
if let Ok(str) = std::str::from_utf8(&value) {
// Attempt to convert into a number
match str.parse::<isize>() {
Ok(number) => params_insert(
&mut params,
field_name,
&field_name_formatted,
Value::Number(Number::from(number)),
),
Err(_) => match str {
"true" => params_insert(
&mut params,
field_name,
&field_name_formatted,
Value::Bool(true),
),
"false" => params_insert(
&mut params,
field_name,
&field_name_formatted,
Value::Bool(false),
),
_ => params_insert(
&mut params,
field_name,
&field_name_formatted,
Value::String(str.to_owned()),
),
},
}
}
} else {
// Nothing
params_insert(&mut params, field_name, &field_name_formatted, Value::Null)
}
}
match serde_json::from_value::<T>(Value::Object(params)) {
Ok(parsed) => Ok(Multipart::<T>::new(parsed)),
Err(err) => Err(match req_owned.app_data::<MultipartConfig>() {
Some(config) => match &config.error_handler {
Some(error_handler) => error_handler(err),
None => actix_web::error::ErrorBadRequest(""),
},
None => actix_web::error::ErrorBadRequest(""),
}),
}
})
}
}
fn params_insert(
params: &mut Map<String, Value>,
field_name: &str,
field_name_formatted: &String,
element: Value,
) {
if params.contains_key(field_name_formatted) {
if let Value::Array(val) = params.get_mut(field_name_formatted).unwrap() {
val.push(element);
}
} else if field_name.ends_with("[]") {
params.insert(field_name_formatted.to_owned(), Value::Array(vec![element]));
} else {
params.insert(field_name_formatted.to_owned(), element);
}
}
type MultipartErrorHandler =
Box<dyn Fn(serde_json::Error) -> actix_web::Error + Send + Sync + 'static>;
/// Config for Multipart data, insert as AppData to actix
pub struct MultipartConfig {
pub error_handler: Option<MultipartErrorHandler>,
}
impl MultipartConfig {
pub fn set_error_handler<F>(mut self, error_handler: F) -> Self
where
F: Fn(serde_json::Error) -> actix_web::Error + Send + Sync + 'static,
{
self.error_handler = Some(Box::new(error_handler));
self
}
}
impl Default for MultipartConfig {
fn default() -> Self {
Self {
error_handler: None,
}
}
}