java - Tomcat:如何使用 Spring Boot 和 Thymeleaf 处理 SizeLimitExceededException
问题描述
我正在使用 Spring Boot 和 Thymeleaf 版本:2.4.10
我有一个非常基本的小项目设置,其中包含用户可以上传文件的联系表格。我正在尝试验证控制器中的用户输入,如下所示:
@RequestMapping(value = "/sendHTMLMail", method = POST)
public String sendHTMLMail(@Valid final ContactEmail contactEmail, BindingResult bindingResult, final Locale locale) throws MessagingException, IOException {
if (bindingResult.hasErrors()) {
return "contactForm.html";
}
this.emailService.sendHTMLMail(contactEmail);
this.emailService.sendHTMLMailToClient(contactEmail);
return "contactForm.html";
}
我的 HTML 模板看起来像这样
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Very Simple Contact Form</title>
<form action="#" th:action="@{/sendHTMLMail}" th:object="${contactEmail}" enctype="multipart/form-data"
method="post">
<label for="clientFirstName">First name:</label><br>
<input type="text" id="clientFirstName" th:field="*{clientFirstName}"><br>
<label th:if="${#fields.hasErrors('clientFirstName')}" th:errors="*{clientFirstName}"></label><br>
<label for="clientLastName">Last name:</label><br>
<input type="text" id="clientLastName" th:field="*{clientLastName}"><br>
<label th:if="${#fields.hasErrors('clientLastName')}" th:errors="*{clientLastName}"></label><br>
<label for="clientEmail">Email address:</label><br>
<input type="text" id="clientEmail" th:field="*{clientEmail}"><br>
<label th:if="${#fields.hasErrors('clientEmail')}" th:errors="*{clientEmail}"></label><br>
<label for="emailText">Message:</label><br>
<input type="text" id="emailText" th:field="*{emailText}"><br>
<label th:if="${#fields.hasErrors('emailText')}" th:errors="*{emailText}"></label><br>
<label for="attachments">Attachments:</label><br>
<input type="file" id="attachments" th:field="*{attachments}" multiple/><br>
<label th:if="${#fields.hasErrors('attachments')}" th:errors="*{attachments}"></label><br>
<input type="submit" name="send" value="Send">
</form>
</head>
<body>
</body>
</html>
我已将我的 tomcat 的属性设置为最大上传大小,如下所示
########################Tomcat file size config############################
spring.servlet.multipart.max-file-size=50MB
spring.servlet.multipart.max-request-size=50MB
server.tomcat.max-swallow-size=50MB
我要验证的模型包含以下属性:
@NotNull
@Size(min = 2, max = 100)
private String clientFirstName;
@NotNull
@Size(min = 2, max = 100)
private String clientLastName;
@NotNull
@Pattern(regexp=".+@.+\\..+", message="Your Email must contain: @[domain].com/net/org etc.")
private String clientEmail;
@NotNull
@Size(min = 10, max = 500)
private String emailText;
@ContactFileConstraint
private MultipartFile[] attachments;
我的自定义验证器如下所示:
public class ClientFileValidator implements
ConstraintValidator<ContactFileConstraint, MultipartFile[]> {
private static final long VALID_FILE_SIZE = 5242880;
@Override
public void initialize(ContactFileConstraint constraintAnnotation) {
ConstraintValidator.super.initialize(constraintAnnotation);
}
@Override
public boolean isValid(MultipartFile[] multipartFiles, ConstraintValidatorContext constraintValidatorContext) {
return isValidLength(multipartFiles) && isValidSize(multipartFiles) && isValidType(multipartFiles);
}
private boolean isValidLength(MultipartFile[] multipartFiles) {
return multipartFiles.length <= 5;
}
private boolean isValidSize(MultipartFile[] multipartFiles) {
for (int file = 0; file < multipartFiles.length; file++) {
if (multipartFiles[file].getSize() > VALID_FILE_SIZE) {
return false;
}
}
return true;
}
private boolean isValidType(MultipartFile[] multipartFiles) {
for (int file = 0; file < multipartFiles.length; file++) {
if (!"image/png".equals(multipartFiles[file].getContentType()) &&
!"image/jpeg".equals(multipartFiles[file].getContentType())) {
return false;
}
}
return true;
}
}
当我尝试上传大于自定义验证器中指定的文件且小于指定的最大上传大小的文件时,一切正常,我可以在重新加载的页面上看到错误,但是当大小超过 50MB 时,Tomcat 本身会拒绝带有SizeLimitExceededException的请求它登陆默认浏览器页面连接中断。这可以用 javascript 解决,所以它对用户更友好,但我想特别问一下是否有办法在 Spring Boot 中处理 tomcat 异常并通过来自服务器端的重定向响应来处理这个问题。我已经查看了其他解决方案关于增加server.tomcat.max-swallow-size的问题,但您可以想象有人总是可以尝试上传更大的文件。
我尝试了以下选项:
- 使用 @ControllerAdvice 注释的自定义类:
@ControllerAdvice
public class FileUploadExceptionAdvisor {
@ExceptionHandler(MaxUploadSizeExceededException.class)
public String maxUploadSizeExceptionHandler(MaxUploadSizeExceededException e, RedirectAttributes redirectAttributes) {
redirectAttributes.addFlashAttribute("attachments", e.getCause().getMessage());
redirectAttributes.addAttribute("contactEmail", new ContactEmail());
return "contactForm";
}
@ExceptionHandler(SizeLimitExceededException.class)
public ResponseEntity<?> handleMaxUploadSizeExceededException(SizeLimitExceededException e){
return ResponseEntity.notFound().build();
}
}
- 在我的控制器中实现HandlerExceptionResolver
@覆盖
public ModelAndView resolveException(
HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse,
Object o,
Exception e) {
ModelAndView modelAndView = new ModelAndView("BasicHTMLFile");
if (e instanceof SizeLimitExceededException) {
logger.error(e);
}
if(e instanceof MaxUploadSizeExceededException){
logger.error(e);
}
return modelAndView;
}
解决方案
推荐阅读
- qt - 如何在 QML 中制作上滑/下滑动画?
- jquery - Spectrum.js 调色板在 UI 中无法正确显示
- xamarin - Xamarin 表单中是否有一个布局,可以根据他们的数量响应地显示它的孩子?
- react-native - 如何在 React Native Paper DataTable 中分页?
- android - 如果我在 putExtra 方法中更改我的包前缀名称,我的前台通知服务无法正常工作,为什么?
- javascript - 自动点击控制台中的某个键
- javascript - 将定制的随机生成器从 JS 移植到 Python 3
- c - 从 C 头文件(函数、结构、...)中提取信息
- angular - 如何为不同应用程序的 Angular 库中的按钮使用不同的标签?
- json - json_normalize 不读取所有数据