2

We have a spring boot server with an embedded jetty that exposes a rest interface. Our RestController service consumes "multipart/form-data" (to upload file) and we use javax.validation (hibernate) to validate the parameter of the request to return BAD_REQUEST when parameters error.

The problem here is that when no parameter is given, an exception is thrown by the filters of jetty and spring and a 500 INTERNAL ERROR will be returned to the client.

We want to return 400 BAD REQUEST in this case but we do not find a proper way to do because the request is rejected before attempting the controller or the parameters validation (we makes @NotNull @RequestParam("file")). So no controllerAdvice will be called

Jetty and Spring filters reject the HTTP request when the type is "multipart/form-data" and there is no attribute (multipart without any part)

What do you suggest for this case?

This is the stack-trace

WARN  2017/07/17 18:15:52.849 CEST <Thread[qtp1020520290-18]> EXCEPTION 
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.web.multipart.MultipartException: Could not parse multipart servlet request; nested exception is java.io.IOException: Missing content for multipart request
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:982) ~[spring-webmvc-4.3.2.RELEASE.jar!/:4.3.2.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:872) ~[spring-webmvc-4.3.2.RELEASE.jar!/:4.3.2.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:707) ~[javax.servlet-api-3.1.0.jar!/:3.1.0]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846) ~[spring-webmvc-4.3.2.RELEASE.jar!/:4.3.2.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:790) ~[javax.servlet-api-3.1.0.jar!/:3.1.0]
at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:845) ~[jetty-servlet-9.3.11.v20160721.jar!/:9.3.11.v20160721]
.........................
Caused by: org.springframework.web.multipart.MultipartException: Could not parse multipart servlet request; nested exception is java.io.IOException: Missing content for multipart request
at org.springframework.web.multipart.support.StandardMultipartHttpServletRequest.parseRequest(StandardMultipartHttpServletRequest.java:111) ~[spring-web-4.3.2.RELEASE.jar!/:4.3.2.RELEASE]
at org.springframework.web.multipart.support.StandardMultipartHttpServletRequest.<init>(StandardMultipartHttpServletRequest.java:85) ~[spring-web-4.3.2.RELEASE.jar!/:4.3.2.RELEASE]
at org.springframework.web.multipart.support.StandardServletMultipartResolver.resolveMultipart(StandardServletMultipartResolver.java:76) ~[spring-web-4.3.2.RELEASE.jar!/:4.3.2.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.checkMultipart(DispatcherServlet.java:1099) ~[spring-webmvc-4.3.2.RELEASE.jar!/:4.3.2.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:932) ~[spring-webmvc-4.3.2.RELEASE.jar!/:4.3.2.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897) ~[spring-webmvc-4.3.2.RELEASE.jar!/:4.3.2.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970) ~[spring-webmvc-4.3.2.RELEASE.jar!/:4.3.2.RELEASE]
... 42 common frames omitted
user1211
  • 1,507
  • 1
  • 18
  • 27
Jugu
  • 787
  • 1
  • 9
  • 23

3 Answers3

2

I didn't succeed to catch the error through HandleException (MultipartException) but by setting MultipartFile as optional (required = false) and then checking if the MultipartFile variable is null.

   ...
   @RequestMapping(value = "/csv", method = RequestMethod.POST)
   @ResponseBody
   public ResponseEntity<?> downloadFile(@RequestParam(value = "file", required = false) MultipartFile file) {
   // Manage the error if no files are uploaded | Not possible to use ExceptionHandler for MultipartFile
   if (file == null) {
      return new ResponseHTTP().WithError("No CSV file selected", HttpStatus.BAD_REQUEST);
   }
   ...
vomnes
  • 131
  • 6
0

This exception occurs as an IOException wrapped in a org.springframework.web.multipart.MultipartException.

Just create a standard Servlet spec error-page error handler for that MultipartException exception type and have your dispatched (as ERROR) page report the status code 400 that you want.

Note, if you do this, you'll also want to catch org.eclipse.jetty.http.BadMessageException in a similar error-page error handler for those similar issues that occur before your spring code is even called.

Also note that there are many types of bad requests, many of which often fall outside of (or before) the servlet context and cannot be handled by springs error handling, so this kind of error setup is very common for your specific needs.

Joakim Erdfelt
  • 46,896
  • 7
  • 86
  • 136
  • I thought to define a servlet filter that catch the exception and make the response but It seems not very clean, already the exception has been wrapped so I get a RuntimeException that wraps an IOException that wraps a MultipartException I have to search inside the stacktrace for a multipartException – Jugu Jul 18 '17 at 08:00
  • Catching exceptions in filters and servlet is not how the servlet spec is designed to work. It has an entire error handling layer of its own, designed to work in the largest variety of situations (incoming error, outgoing error, wrapped request error, wrapped response error, async processing errors, async i/o errors, off dispatch thread errors, etc) – Joakim Erdfelt Jul 18 '17 at 13:28
0

You can write your own ControllerAdvice, something like this.

    @ResponseBody
    @ExceptionHandler(MultipartException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ErrorObject handleMultiPartException(
            MultipartException e) {
        //process error message
        return new ErrorObject(e);
    }
  • The request is rejected before attempting the controller so a controllerAdvice will not be used – Jugu Jul 18 '17 at 08:01