Refine MaxUploadSizeExceededException handling

This commit refines MaxUploadSizeExceededException
handling in order to translate to a "413 Payload Too Large"
status code instead of "500 Internal Server Error", with
related ProblemDetail body.

Closes gh-27170
This commit is contained in:
Sébastien Deleuze
2023-10-25 12:06:55 +02:00
parent 7582bd8667
commit 3b80f2c4cb
4 changed files with 63 additions and 2 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,17 +16,26 @@
package org.springframework.web.multipart;
import org.springframework.http.HttpStatus;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.ProblemDetail;
import org.springframework.lang.Nullable;
import org.springframework.web.ErrorResponse;
/**
* MultipartException subclass thrown when an upload exceeds the
* maximum upload size allowed.
*
* @author Juergen Hoeller
* @author Sebastien Deleuze
* @since 1.0.1
*/
@SuppressWarnings("serial")
public class MaxUploadSizeExceededException extends MultipartException {
public class MaxUploadSizeExceededException extends MultipartException implements ErrorResponse {
private static final ProblemDetail body =
ProblemDetail.forStatusAndDetail(HttpStatus.PAYLOAD_TOO_LARGE, "Maximum upload size exceeded");
private final long maxUploadSize;
@@ -60,4 +69,14 @@ public class MaxUploadSizeExceededException extends MultipartException {
return this.maxUploadSize;
}
@Override
public HttpStatusCode getStatusCode() {
return HttpStatus.PAYLOAD_TOO_LARGE;
}
@Override
public ProblemDetail getBody() {
return body;
}
}

View File

@@ -50,6 +50,7 @@ import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.context.request.async.AsyncRequestTimeoutException;
import org.springframework.web.method.annotation.HandlerMethodValidationException;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.multipart.support.MissingServletRequestPartException;
import org.springframework.web.servlet.NoHandlerFoundException;
import org.springframework.web.servlet.resource.NoResourceFoundException;
@@ -128,6 +129,7 @@ public abstract class ResponseEntityExceptionHandler implements MessageSourceAwa
NoResourceFoundException.class,
AsyncRequestTimeoutException.class,
ErrorResponseException.class,
MaxUploadSizeExceededException.class,
ConversionNotSupportedException.class,
TypeMismatchException.class,
HttpMessageNotReadableException.class,
@@ -176,6 +178,9 @@ public abstract class ResponseEntityExceptionHandler implements MessageSourceAwa
else if (ex instanceof ErrorResponseException subEx) {
return handleErrorResponseException(subEx, subEx.getHeaders(), subEx.getStatusCode(), request);
}
else if (ex instanceof MaxUploadSizeExceededException subEx) {
return handleMaxUploadSizeExceededException(subEx, subEx.getHeaders(), subEx.getStatusCode(), request);
}
// Lower level exceptions, and exceptions used symmetrically on client and server
@@ -435,6 +440,24 @@ public abstract class ResponseEntityExceptionHandler implements MessageSourceAwa
return handleExceptionInternal(ex, null, headers, status, request);
}
/**
* Customize the handling of any {@link MaxUploadSizeExceededException}.
* <p>This method delegates to {@link #handleExceptionInternal}.
* @param ex the exception to handle
* @param headers the headers to use for the response
* @param status the status code to use for the response
* @param request the current request
* @return a {@code ResponseEntity} for the response to use, possibly
* {@code null} when the response is already committed
* @since 6.1
*/
@Nullable
protected ResponseEntity<Object> handleMaxUploadSizeExceededException(
MaxUploadSizeExceededException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) {
return handleExceptionInternal(ex, null, headers, status, request);
}
/**
* Customize the handling of {@link ConversionNotSupportedException}.
* <p>By default this method creates a {@link ProblemDetail} with the status

View File

@@ -61,6 +61,7 @@ import org.springframework.web.context.request.WebRequest;
import org.springframework.web.context.request.async.AsyncRequestTimeoutException;
import org.springframework.web.context.support.StaticWebApplicationContext;
import org.springframework.web.method.annotation.HandlerMethodValidationException;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.multipart.support.MissingServletRequestPartException;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.ModelAndView;
@@ -78,6 +79,7 @@ import static org.mockito.BDDMockito.mock;
* Unit tests for {@link ResponseEntityExceptionHandler}.
*
* @author Rossen Stoyanchev
* @author Sebastien Deleuze
*/
public class ResponseEntityExceptionHandlerTests {
@@ -290,6 +292,11 @@ public class ResponseEntityExceptionHandlerTests {
testException(new AsyncRequestTimeoutException());
}
@Test
public void maxUploadSizeExceededException() {
testException(new MaxUploadSizeExceededException(1000));
}
@Test
public void controllerAdvice() throws Exception {
StaticWebApplicationContext ctx = new StaticWebApplicationContext();

View File

@@ -44,6 +44,7 @@ import org.springframework.web.bind.MissingPathVariableException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.context.request.async.AsyncRequestTimeoutException;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.multipart.support.MissingServletRequestPartException;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
@@ -58,6 +59,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* Unit tests for {@link DefaultHandlerExceptionResolver}.
*
* @author Arjen Poutsma
* @author Sebastien Deleuze
*/
public class DefaultHandlerExceptionResolverTests {
@@ -246,6 +248,16 @@ public class DefaultHandlerExceptionResolverTests {
assertThat(response.getStatus()).as("Invalid status code").isEqualTo(503);
}
@Test
public void handleMaxUploadSizeExceededException() {
MaxUploadSizeExceededException ex = new MaxUploadSizeExceededException(1000);
ModelAndView mav = exceptionResolver.resolveException(request, response, null, ex);
assertThat(mav).as("No ModelAndView returned").isNotNull();
assertThat(mav.isEmpty()).as("No Empty ModelAndView returned").isTrue();
assertThat(response.getStatus()).as("Invalid status code").isEqualTo(413);
assertThat(response.getErrorMessage()).isEqualTo("Maximum upload size exceeded");
}
@Test
public void customModelAndView() {
ModelAndView expected = new ModelAndView();