How can I gracefully responds as 413 when DefaultBodyLimit fails? #1847
-
Hi! I'm writing a simple file upload server. So I used async fn upload_file(
WithRejection(mut body, _): WithRejection<Multipart, FileRouterError>,
) -> Result<(StatusCode, Json<Vec<FileUpload>>), FileRouterError> {
let mut uuids = Vec::new();
while let Some(field) = body.next_field().await? {
let uuid = steam_to_file(field).await?;
uuids.push(FileUpload {
uuid: uuid.to_string(),
});
}
Ok((StatusCode::CREATED, Json(uuids)))
}
async fn steam_to_file<E>(
stream: impl Stream<Item = Result<Bytes, E>>,
) -> Result<Uuid, FileRouterError>
where
FileRouterError: From<E>,
{
let uuid = uuid::Uuid::new_v4();
let path = Path::new("files").join(uuid.to_string());
let mut file = BufWriter::new(File::create(path).await?);
futures::pin_mut!(stream);
while let Some(chunk) = stream.try_next().await? {
file.write_all(&chunk).await?;
}
Ok(uuid)
} But I'm surprised that my server is responding #[derive(Error, Debug)]
pub enum FileRouterError {
#[error("invalid multipart request")]
MultipartExtractorRejection(#[from] MultipartRejection),
#[error("invalid multipart request")]
MultipartError(#[from] MultipartError),
#[error("internal server error")]
IOError(#[from] std::io::Error),
}
impl IntoResponse for FileRouterError {
fn into_response(self) -> Response {
#[cfg(debug_assertions)]
let body = Json(json!({ "error": format!("{:?}", self) }));
#[cfg(not(debug_assertions))]
let body = Json(json!({
"error": self.to_string()
}));
let status_code = match self {
FileRouterError::MultipartExtractorRejection(_) => StatusCode::BAD_REQUEST,
FileRouterError::MultipartError(err) => {
status_code_from_multer_error(err.into_multer())
}
FileRouterError::IOError(_) => StatusCode::INTERNAL_SERVER_ERROR,
};
(status_code, body).into_response()
}
}
fn status_code_from_multer_error(err: axam::multer::Error) -> StatusCode {
match err {
axam::multer::Error::UnknownField { .. } => StatusCode::BAD_REQUEST,
axam::multer::Error::IncompleteFieldData { .. } => StatusCode::BAD_REQUEST,
axam::multer::Error::IncompleteHeaders => StatusCode::BAD_REQUEST,
axam::multer::Error::ReadHeaderFailed(..) => StatusCode::BAD_REQUEST,
axam::multer::Error::DecodeHeaderName { .. } => StatusCode::BAD_REQUEST,
axam::multer::Error::DecodeHeaderValue { .. } => StatusCode::BAD_REQUEST,
axam::multer::Error::IncompleteStream => StatusCode::BAD_REQUEST,
axam::multer::Error::FieldSizeExceeded { .. } => StatusCode::PAYLOAD_TOO_LARGE,
axam::multer::Error::StreamSizeExceeded { .. } => StatusCode::PAYLOAD_TOO_LARGE,
axam::multer::Error::StreamReadFailed(err) => {
match err.downcast_ref::<axam::multer::Error>() {
Some(_) => {
let err = *err.downcast::<axam::multer::Error>().unwrap();
return status_code_from_multer_error(err);
}
None => {}
}
match err.downcast_ref::<axam::Error>() {
Some(_) => {
let err = *err.downcast::<axam::Error>().unwrap();
return status_code_from_axum_error(err);
}
None => {}
}
StatusCode::INTERNAL_SERVER_ERROR
}
axam::multer::Error::LockFailure => StatusCode::INTERNAL_SERVER_ERROR,
axam::multer::Error::NoMultipart => StatusCode::BAD_REQUEST,
axam::multer::Error::DecodeContentType(..) => StatusCode::BAD_REQUEST,
axam::multer::Error::NoBoundary => StatusCode::BAD_REQUEST,
_ => StatusCode::BAD_REQUEST,
}
}
fn status_code_from_axum_error(err: axam::Error) -> StatusCode {
let err = err.into_inner();
match err.downcast_ref::<axam::extract::rejection::LengthLimitError>() {
Some(_) => return StatusCode::PAYLOAD_TOO_LARGE,
None => {}
}
match err.downcast_ref::<http_body::LengthLimitError>() {
Some(_) => return StatusCode::PAYLOAD_TOO_LARGE,
None => {}
}
match err.downcast_ref::<axam::Error>() {
Some(_) => {
let err = *err.downcast::<axam::Error>().unwrap();
return status_code_from_axum_error(err);
}
None => {}
}
StatusCode::INTERNAL_SERVER_ERROR
} Above code works as I expected, but in my opinion, this kind of work isn't good. This may not work when the |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment
-
Yeah I agree that's a lot of code to write. I guess it makes implement I've filed an issue for it #1851 |
Beta Was this translation helpful? Give feedback.
Yeah I agree that's a lot of code to write. I guess it makes implement
IntoResponse
forMultipartError
. It should probably also havestatus_code
andbody_text
methods like all rejections have (https://docs.rs/axum/latest/axum/extract/multipart/enum.MultipartRejection.html#method.body_text)I've filed an issue for it #1851