0

While working on a personal project on Spring Boot, I have to develop a web service that accepts a file and some metadata, and I have decided to test with a Postman client.

Versions I am using:

  • Postman version: Postman for Mac Version 7.8.0 OS X 18.7.0 / x64
  • Java version: 1.8.0_212
  • Spring boot version: 2.1.1.RELEASE

As metadata will potentially be quite structured, I have decided to use the multipart/form-data content type, consisting in two parts:

  • a "file" part containing the file
  • a "body" part containing the json with the metadata

I've configured the call in Postman like this postman screenshot

Now, Spring boot configuration. First of all, I've added the following lines to application.properties:

## MULTIPART (MultipartProperties)
# Enable multipart uploads
spring.servlet.multipart.enabled=true
# Threshold after which files are written to disk.
spring.servlet.multipart.file-size-threshold=2KB
# Max file size.
spring.servlet.multipart.max-file-size=20MB
# Max Request Size
spring.servlet.multipart.max-request-size=25MB

I've then created my controller with the endpoint

@PostMapping(Paths.Registrations.BASE)
@ResponseBody
public PostRegistrationResponseDto postRegistration(@RequestParam("file") MultipartFile file, @RequestParam("body") PostRegistrationRequestDto req) {
    PostRegistrationResponseDto resp = new PostRegistrationResponseDto();
    resp.setId(new Random().nextLong());
    resp.setFileName(req.getFileName());
    resp.setRegistrationTime(LocalDateTime.now());

    return resp;
}

Unfortunately, the call does not even arrive to Spring: I receive a HTTP 500 error with this body

<!doctype html>
<html lang="en">

<head>
    <title>HTTP Status 500 – Internal Server Error</title>
    <style type="text/css">
        ...
    </style>
</head>

<body>
    <h1>HTTP Status 500 – Internal Server Error</h1>
</body>

</html>

In my server log I get no exception, but when I set the debugger to block on exceptions I saw that tomcat launches an EOF exception with this stack trace

java.io.EOFException
at org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper.fillReadBuffer(NioEndpoint.java:1208)
at org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper.read(NioEndpoint.java:1142)
at org.apache.coyote.http11.Http11InputBuffer.fill(Http11InputBuffer.java:729)
at org.apache.coyote.http11.Http11InputBuffer.parseRequestLine(Http11InputBuffer.java:352)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:294)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:791)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1417)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)

I admit I do not even know where to start from... can someone give me some hints of what I'm doing wrong?

Thanks :)

CodingMonkey
  • 143
  • 3
  • 14

1 Answers1

0

In the end, what I discovered is that Tomcat throws a lot of exceptions, not always meaningful.

The problem I had was not related to that exception, but to the fact that Spring doesn't automatically map the String part to the Dto.

To make everything work, I had to write this component:

package it.aegidea.proofy.api.converters;

import com.fasterxml.jackson.databind.ObjectMapper;
import it.aegidea.proofy.api.dtos.proofyapi.PostRegistrationRequestDto;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;

import java.io.IOException;

@Component
public class PostRegistrationRequestDtoConverter implements Converter<String, PostRegistrationRequestDto> {

    private final ObjectMapper objectMapper;

    @Autowired
    public PostRegistrationRequestDtoConverter(final ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
    }

    @Override
    public PostRegistrationRequestDto convert(String source) {
        try {
            return objectMapper.readValue(source, PostRegistrationRequestDto.class);
        } catch (IOException e) {
            return null;
        }
    }

}

This way, Spring understood how to map the string to a PostRegistrationRequestDto and could successfully map the request to the types on the endpoint.

CodingMonkey
  • 143
  • 3
  • 14