I am facing a Whitelabel Error
Page stating:
There was an unexpected error (type=Internal Server Error, status=500).
Failed to parse multipart servlet request
org.springframework.web.multipart.MultipartException: Failed to parse multipart servlet request
And after multiple checking, I realised that this is not occurring because there is some problem while uploading files, but instead, is occurring when I am inputting a true/false value through a checkbox. Everything works fine until the checkbox is clicked. As soon as the checkbox is clicked and I submit the form, the request doesn't even go to the controller. It immediately breaks because it is unable to parse.
Here is my entire HTML Code:
(The checkbox I am referring to is the Mark as Favourite
.
<!doctype html>
<html lang="en" th:replace="~{base::parent(~{::#content}, ~{:: title}, ~{:: script})}">
<head>
<title th:text="${user.name + ' | Add Contacts'}">Add Contacts</title>
</head>
<body>
<div id="content" class="pl-64 pt-24">
<div th:replace="~{user/sidebar::sidebar}"></div>
<div class="pl-4 pr-4 md:pl-10">
<div class="grid grid-cols-1 md:grid-cols-12 gap-6">
<div class="md:col-span-3">
<div
class="card block m-20 mt-0 p-6 bg-white border border-gray-200 rounded-lg shadow-sm hover:bg-gray-100 hover:shadow-md
dark:bg-gray-800 dark:border-gray-700 dark:hover:bg-gray-700 transition-all duration-500 ease-in-out">
<div th:replace="~{message::messageBox}"></div>
<h1 class="text-2xl font-semibold text-gray-900 dark:text-white mb-4">Add New Contacts</h1>
<p class="text-gray-900 dark:text-gray-300 mb-10">Fill in the details below to add new contacts.
</p>
<form th:action="@{'/user/contacts/save'}" method="post" th:object="${contactForm}"
enctype="multipart/form-data" novalidate>
<div class="grid gap-6 mb-6 md:grid-cols-2">
<!-- Name -->
<div>
<label for="name"
class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Full
Name</label>
<input type="text" id="name" name="name" th:field="*{name}" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500
block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400
dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
placeholder="Full Name" required/>
<p th:if="${#fields.hasErrors('name')}" th:errors="*{name}"
class="text-red-500 text-sm font-medium mt-1"></p>
</div>
<!-- Email -->
<div>
<label for="email"
class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Email
address</label>
<input type="email" id="email" name="email" th:field="*{email}" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500
block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400
dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
placeholder="name@company.com" required />
<p th:if="${#fields.hasErrors('email')}" th:errors="*{email}"
class="text-red-500 text-sm font-medium mt-1"></p>
</div>
<!-- Phone -->
<div>
<label for="phoneNumber"
class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Phone
Number</label>
<input type="tel" id="phoneNumber" name="phoneNumber" th:field="*{phoneNumber}"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500
block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400
dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
placeholder="XXXXXXXXXX" pattern="[0-9]{10}" required />
<p th:if="${#fields.hasErrors('phoneNumber')}" th:errors="*{phoneNumber}"
class="text-red-500 text-sm font-medium mt-1"></p>
</div>
<!-- Address -->
<div>
<label for="address"
class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Address</label>
<input type="text" id="address" name="address" th:field="*{address}" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500
block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400
dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
placeholder="123, MG Road, Bangalore" required />
<p th:if="${#fields.hasErrors('address')}" th:errors="*{address}"
class="text-red-500 text-sm font-medium mt-1"></p>
</div>
<!-- Website Link-->
<div>
<label for="websiteLink"
class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Website</label>
<input type="url" id="websiteLink" name="websiteLink" th:field="*{websiteLink}"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500
block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400
dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
placeholder="https://example.com" />
<p th:if="${#fields.hasErrors('websiteLink')}" th:errors="*{websiteLink}"
class="text-red-500 text-sm font-medium mt-1"></p>
</div>
<!-- LinkedIn Link-->
<div>
<label for="linkedinLink"
class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">LinkedIn</label>
<input type="url" id="linkedinLink" name="linkedinLink" th:field="*{linkedinLink}"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500
block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400
dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
placeholder="https://linkedin.com/in/username" />
<p th:if="${#fields.hasErrors('linkedinLink')}" th:errors="*{linkedinLink}"
class="text-red-500 text-sm font-medium mt-1"></p>
</div>
<!-- Picture Upload -->
<div>
<label for="contactImage"
class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Upload
Picture</label>
<input accept="image/*"
class="block w-full text-sm text-gray-900 border border-gray-300 rounded-lg cursor-pointer bg-gray-50
dark:text-gray-400 focus:outline-none dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400"
id="contactImage" name="contactImage" th:field="*{contactImage}"
type="file"/>
<p class="mt-1 text-xs text-gray-500 dark:text-gray-300">PNG, JPG or JPEG (max ~5MB)
</p>
<p class="text-red-500 text-sm font-medium mt-1" th:errors="*{contactImage}"
th:if="${#fields.hasErrors('contactImage')}"></p>
</div>
<!-- Favourite -->
<div class="flex items-center mt-8">
<input type="checkbox" id="favourite" name="favourite" th:field="*{favourite}"
value="true"
class="cursor-pointer h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-blue-300
dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800" />
<label for="favourite"
class="ms-2 text-sm font-medium text-gray-900 dark:text-gray-300">Mark as
Favourite</label>
</div>
</div>
<!-- Description -->
<div class="mb-6">
<label for="description"
class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Description</label>
<textarea id="description" name="description" th:field="*{description}" rows="4" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500
block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400
dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
placeholder="Enter a short bio or note about the person here..."></textarea>
<p th:if="${#fields.hasErrors('description')}" th:errors="*{description}"
class="text-red-500 text-sm font-medium mt-1"></p>
</div>
<!-- Submit -->
<button class="cursor-pointer bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300
font-medium rounded-lg text-sm w-full sm:w-auto px-5 py-2.5 text-center
dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800" name="submit" type="submit">
Add Contact
</button>
</form>
</div>
</div>
</div>
</div>
</div>
<script>console.log("Add Contacts Page")</script>
</body>
</html>
P.S. -> The Thymeleaf Fragments work properly
Here is my ContactForm.java
package com.sathwikhbhat.scm.forms;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.web.multipart.MultipartFile;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class ContactForm {
@Size(min = 3, message = "Name must be minimum 3 characters")
private String name;
@Email(regexp = "^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$", message = "Invalid email address")
private String email;
@Pattern(regexp = "^[6-9]\\d{9}$", message = "Invalid phone number")
private String phoneNumber;
@Size(min = 10, message = "Address must be minimum 10 characters")
private String address;
private String websiteLink;
private String linkedinLink;
private MultipartFile contactImage;
private boolean favourite;
@Size(max = 500, message = "Description must be maximum 500 characters")
private String description;
}
I suspect the issue may be related to how Spring parses boolean checkbox values inside a multipart/form-data request. Because my previous form was working fine, and there I did not request a file upload. As soon as I switched the form to multipart/form-data
to support file uploads, checking the checkbox started breaking the request.
Did you recently update to Spring Boot 3.5.1 or Tomcat 10.1? If so, this might be related to stricter limits for multipart requests.
You can change the limit (e.g. from 10 to 30) by setting the following property:
server:
tomcat:
max-part-count: 30
OR
server.tomcat.max-part-count=30
See https://github.com/spring-projects/spring-boot/releases/tag/v3.5.1