Spring Boot 2.7.5中Multipart上传超限时自定义响应的实现问题
解决Spring Boot 2.7.5 Multipart文件上传超出大小返回自定义响应的问题
问题根源
你遇到的核心问题是:Multipart请求的大小校验是在Servlet容器层面完成的(从错误码SRVE8021E判断,你使用的是WebSphere容器),这个校验发生在Spring的DispatcherServlet处理请求之前,因此Spring的@ControllerAdvice无法捕获容器抛出的异常,导致你的自定义处理逻辑执行后被容器默认错误响应覆盖。
可行解决方案
方案一:配置WebSphere容器的自定义错误处理
针对WebSphere容器,通过Spring Boot的容器配置类绑定错误映射,将容器抛出的超大文件异常导向自定义端点:
- 配置WebSphere容器错误映射:
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory import org.springframework.boot.web.embedded.websphere.WebSphereServletWebServerFactory import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration @Configuration class WebServerConfig { @Bean fun webServerFactory(): ConfigurableServletWebServerFactory { val factory = WebSphereServletWebServerFactory() // 将容器抛出的IllegalStateException(对应超大文件错误)映射到自定义端点 factory.addErrorPages(org.springframework.boot.web.servlet.error.ErrorPage( IllegalStateException::class.java, "/api/errors/file-too-large" )) return factory } }
- 编写自定义错误端点返回响应:
import org.springframework.http.HttpStatus import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.RestController @RestController class CustomErrorController( private val multipartProperties: org.springframework.boot.autoconfigure.web.servlet.MultipartProperties ) { @GetMapping("/api/errors/file-too-large") fun handleFileTooLarge(): ResponseEntity<CustomErrorDto> { val fileSizeMegabyte = multipartProperties.maxFileSize.toMegabytes().toString() val message = "文件过大,上传文件不能超过${fileSizeMegabyte}MB" return ResponseEntity( CustomErrorDto("FILE_TOO_LARGE", message, mapOf("maxFileSize" to fileSizeMegabyte)), HttpStatus.BAD_REQUEST ) } }
方案二:自定义MultipartResolver主动校验大小
绕过容器提前校验,在Spring层面实现文件大小检查,让@ControllerAdvice能捕获异常:
- 自定义MultipartResolver:
import org.springframework.web.multipart.MaxUploadSizeExceededException import org.springframework.web.multipart.MultipartException import org.springframework.web.multipart.MultipartHttpServletRequest import org.springframework.web.multipart.commons.CommonsMultipartResolver import javax.servlet.http.HttpServletRequest class CustomMultipartResolver( private val maxFileSize: Long ) : CommonsMultipartResolver() { override fun resolveMultipart(request: HttpServletRequest): MultipartHttpServletRequest { try { val multipartRequest = super.resolveMultipart(request) // 遍历所有文件检查大小 multipartRequest.fileMap.values.forEach { file -> if (file.size > maxFileSize) { throw MaxUploadSizeExceededException(maxFileSize) } } return multipartRequest } catch (e: Exception) { if (e is MultipartException) { throw e } throw MultipartException("文件解析失败", e) } } }
- 将自定义Resolver注入Spring容器:
import org.springframework.boot.autoconfigure.web.servlet.MultipartProperties import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import org.springframework.web.multipart.MultipartResolver @Configuration class MultipartConfig( private val multipartProperties: MultipartProperties ) { @Bean(name = ["multipartResolver"]) fun multipartResolver(): MultipartResolver { return CustomMultipartResolver(multipartProperties.maxFileSize.toBytes()) } }
- 开启延迟解析(保持你之前配置的
spring.servlet.multipart.resolve-lazily: true),让文件解析延迟到Controller处理阶段,确保Spring异常处理器能捕获异常。
方案三:使用Filter提前拦截请求大小
在容器解析Multipart之前,通过Filter检查请求Content-Length,超过阈值直接返回自定义响应:
import org.springframework.http.HttpStatus import org.springframework.http.MediaType import org.springframework.stereotype.Component import javax.servlet.* import javax.servlet.http.HttpServletRequest import javax.servlet.http.HttpServletResponse @Component class MultipartSizeCheckFilter( private val multipartProperties: org.springframework.boot.autoconfigure.web.servlet.MultipartProperties ) : Filter { override fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) { val httpRequest = request as HttpServletRequest val contentType = httpRequest.contentType // 仅处理Multipart类型请求 if (contentType?.startsWith("multipart/form-data") == true) { val contentLength = httpRequest.contentLengthLong val maxSize = multipartProperties.maxRequestSize.toBytes() if (contentLength > maxSize) { val httpResponse = response as HttpServletResponse val fileSizeMegabyte = multipartProperties.maxFileSize.toMegabytes().toString() val errorMessage = """{"code":"FILE_TOO_LARGE","message":"文件过大,上传文件不能超过${fileSizeMegabyte}MB","maxFileSize":"${fileSizeMegabyte}"}""" httpResponse.status = HttpStatus.BAD_REQUEST.value() httpResponse.contentType = MediaType.APPLICATION_JSON_VALUE httpResponse.writer.write(errorMessage) return } } chain.doFilter(request, response) } }
注意:该方案依赖客户端准确发送Content-Length请求头,若客户端未发送或值不准确,校验会失效。
内容的提问来源于stack exchange,提问作者MrMyth
相关产品推荐
相关产品推荐

