From 3b80f2c4cb92d43b90c36f6823f4bacd6f67cb83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Deleuze?= Date: Wed, 25 Oct 2023 12:06:55 +0200 Subject: [PATCH] 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 --- .../MaxUploadSizeExceededException.java | 23 +++++++++++++++++-- .../ResponseEntityExceptionHandler.java | 23 +++++++++++++++++++ .../ResponseEntityExceptionHandlerTests.java | 7 ++++++ .../DefaultHandlerExceptionResolverTests.java | 12 ++++++++++ 4 files changed, 63 insertions(+), 2 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/web/multipart/MaxUploadSizeExceededException.java b/spring-web/src/main/java/org/springframework/web/multipart/MaxUploadSizeExceededException.java index 9d8c4f18cf..f781b7ef6e 100644 --- a/spring-web/src/main/java/org/springframework/web/multipart/MaxUploadSizeExceededException.java +++ b/spring-web/src/main/java/org/springframework/web/multipart/MaxUploadSizeExceededException.java @@ -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; + } + } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityExceptionHandler.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityExceptionHandler.java index 529261b569..cc9e510bd6 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityExceptionHandler.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityExceptionHandler.java @@ -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}. + *

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 handleMaxUploadSizeExceededException( + MaxUploadSizeExceededException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) { + + return handleExceptionInternal(ex, null, headers, status, request); + } + /** * Customize the handling of {@link ConversionNotSupportedException}. *

By default this method creates a {@link ProblemDetail} with the status diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityExceptionHandlerTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityExceptionHandlerTests.java index 4767bb825d..b3967ebc18 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityExceptionHandlerTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityExceptionHandlerTests.java @@ -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(); diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/support/DefaultHandlerExceptionResolverTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/support/DefaultHandlerExceptionResolverTests.java index 163fd5c064..5ac0a6fd8c 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/support/DefaultHandlerExceptionResolverTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/support/DefaultHandlerExceptionResolverTests.java @@ -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();