Impl OptionalFromRequest on MultipartFile and change behaviour on MultipartFiles to contain 0 files

This commit is contained in:
2025-09-06 15:28:24 +02:00
parent e0500b8e97
commit 113011399b
6 changed files with 1201 additions and 572 deletions

View File

@ -1,10 +1,11 @@
use axum::{
extract::{
FromRequest, Multipart, Request,
multipart::{Field, MultipartError, MultipartRejection},
},
response::IntoResponse,
};
use axum::extract::FromRequest;
use axum::extract::Multipart;
use axum::extract::OptionalFromRequest;
use axum::extract::Request;
use axum::extract::multipart::Field;
use axum::extract::multipart::MultipartError;
use axum::extract::multipart::MultipartRejection;
use axum::response::IntoResponse;
use mime::Mime;
use std::str::FromStr;
use thiserror::Error;
@ -140,6 +141,40 @@ where
}
}
impl<S> OptionalFromRequest<S> for MultipartFile
where
S: Send + Sync,
{
type Rejection = MultipartFileRejection;
/// Extracts a single file from a multipart request.
/// Expects exactly one file. A file must have a name, bytes and optionally a content type.
/// This extractor consumes the request and must ble placed last in the handler.
/// # Example
/// ```
/// use std::io::Read;
/// use axum::response::Html;
/// use lib::axum::extractor::{MultipartFile, MultipartFiles};
///
/// async fn upload_file(opt_file: Option<MultipartFile>) -> Html<String> {
/// Html(opt_file
/// .map(|MultipartFile(file)| String::from_utf8(file.bytes).unwrap())
/// .unwrap_or_else(|| String::from("<p>Not Found</p>"))
/// )
/// }
/// ```
async fn from_request(req: Request, state: &S) -> Result<Option<Self>, Self::Rejection> {
let multipart = Multipart::from_request(req, state).await?;
let files = get_files(multipart).await?;
if files.len() > 1 {
Err(MultipartFileRejection::SeveralFiles)
} else {
let file = files.first().ok_or(MultipartFileRejection::NoFiles)?;
Ok(Some(MultipartFile(file.clone())))
}
}
}
impl<S> FromRequest<S> for MultipartFiles
where
S: Send + Sync,
@ -147,7 +182,7 @@ where
type Rejection = MultipartFileRejection;
/// Extracts multiple files from a multipart request.
/// Expects at least one file. A file must have a name, bytes and optionally a content type.
/// Can contain 0 files. A file must have a name, bytes and optionally a content type.
/// This extractor consumes the request and must ble placed last in the handler.
/// # Example
/// ```
@ -167,11 +202,7 @@ where
async fn from_request(req: Request, state: &S) -> Result<Self, Self::Rejection> {
let multipart = Multipart::from_request(req, state).await?;
let files = get_files(multipart).await?;
if files.is_empty() {
Err(MultipartFileRejection::NoFiles)
} else {
Ok(MultipartFiles(files))
}
Ok(MultipartFiles(files))
}
}