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, } } }