I made a question before to receive a list of custom objects on Spring Boot on multipart/form-data requests. Now, I need to make some adjustments to send files as well
Before, I was sending the list field as a JSON, so a custom converter was enough to handle the task. But it won't work for files. So I found this answer on how to handle the POST of arrays/lists of objects (they used NextJS as the backend) and I'd like to do something similar for Spring, and can't find any answers as well. Here is an exemple of how the request should look:
Or, if you need a JS example:
function createFormData() {
const formDate = new FormData()
formDate.append('name', 'test')
formDate.append('logo', new File([''], 'test.png'))
formDate.append('link', 'test')
formDate.append('contacts[0][name]', 'test')
formDate.append('contacts[0][email]', 'test')
formDate.append('contacts[0][icon]', new File([''], 'test.png'))
formDate.append('contacts[1][name]', 'test')
formDate.append('contacts[1][email]', 'test')
formDate.append('contacts[1][icon]', new File([''], 'test.png'))
return formDate
}
When receiving a request like this, Spring returns:
Invalid property 'contacts[0][email]' of bean class [com.example.trilha.dto.PartnerRequest]: Illegal attempt to get property 'contacts' threw exception
How can I implement this?
My route is the following:
@PostMapping(value = "createPartner", consumes = "multipart/form-data")
public ResponseEntity<String> createPartner(
@ModelAttribute PartnerRequest partnerRequest
)
The DTOs for the request are the following:
record Contacts(
String name,
String email,
MultipartFile icon
) {}
public record PartnerRequest(
String name,
MultipartFile logo,
String link,
List<Contact> contacts
){}
EDIT: I do not have a front-end right now, I'm creating and testing the API first before build the interface
Changing ModelAttribute
to RequestParam
does not solve the problem, because Spring will try to read as a JSON (thus won't handle the file upload)
So, I found a way to do this and it isn't good in any way. Won't work if you need to nest it with something else and is more code to mantain
Essentially, I take the request and parse it myself
@PostMapping(value = "createPartner", consumes = "multipart/form-data")
public ResponseEntity<String> createPartner(
MultipartHttpServletRequest request
) throws Exception {
try {
PartnerRequest prq = PartnerRequestParser.parse(request);
return ResponseEntity.ok("Ok");
} catch (Exception e) {
return ResponseEntity.internalServerError().body(e.getMessage());
}
}
The parser is trivial code, you take each field and assign to the object:
String name = request.getParameter("name");
return new PartnerRequest(name, ...);
For the list of objects, you have to iterate over each index like this:
List<Contacts> contacts = new ArrayList<>();
int index = 0;
while (request.getParameter("contacts[" + index + "][name]") != null) {
contacts.add(new Contacts(
request.getParameter("contacts[" + index + "][name]"),
request.getFile("contacts[" + index + "][icon]"),
));
index++;
}