处理没有参数的“multipart/form-data”请求异常

Posted

技术标签:

【中文标题】处理没有参数的“multipart/form-data”请求异常【英文标题】:Handling exception of "multipart/form-data" request with no params 【发布时间】:2017-12-22 06:54:04 【问题描述】:

我们有一个带有嵌入式码头的 Spring Boot 服务器,该码头公开了一个休息接口。 我们的 RestController 服务使用 "multipart/form-data"(上传文件),我们使用javax.validation(休眠)来验证请求的参数以在何时返回 BAD_REQUEST参数错误。

这里的问题是,当不给参数时,jetty和spring的过滤器会抛出异常,并且会返回一个500 INTERNAL ERROR给客户端。

在这种情况下,我们希望返回 400 BAD REQUEST,但我们找不到合适的方法,因为在尝试控制器或参数验证之前请求被拒绝(我们使 @NotNull @RequestParam (“文件”))。所以不会调用controllerAdvice

当类型为“multipart/form-data”且没有属性(multipart without any part)时,Jetty 和 Spring 过滤器拒绝 HTTP 请求

你对这个案例有什么建议?

这是堆栈跟踪

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

【问题讨论】:

你的web.xml?中有multipart-*标签 当我给一个文件时它工作得很好;该文件确实已上传,因此配置正确。我使用没有 web.xml 的 spring boot 【参考方案1】:

此异常发生为 IOException 包裹在 org.springframework.web.multipart.MultipartException 中。

只需为 MultipartException 异常类型创建一个标准 Servlet 规范 error-page 错误处理程序,并让您的调度(作为错误)页面报告您想要的状态代码 400。

请注意,如果您这样做,您还需要在类似的 error-page 错误处理程序中捕获 org.eclipse.jetty.http.BadMessageException,以处理在调用 spring 代码之前发生的类似问题。

另外请注意,有很多类型的错误请求,其中许多通常位于 servlet 上下文之外(或之前),并且无法通过 springs 错误处理来处理,因此这种错误设置对于您的特定需求非常常见.

【讨论】:

我想定义一个 servlet 过滤器来捕获异常并做出响应,但它似乎不是很干净,已经包装了异常,所以我得到了一个包装了 IOException 的 RuntimeException 包装了 MultipartException 我必须在堆栈跟踪中搜索 multipartException 在过滤器和 servlet 中捕获异常并不是 servlet 规范的工作方式。它有自己的完整错误处理层,旨在在最广泛的情况下工作(传入错误、传出错误、包装请求错误、包装响应错误、异步处理错误、异步 i/o 错误、关闭调度线程错误、等)【参考方案2】:

您可以编写自己的 ControllerAdvice,类似这样。

    @ResponseBody
    @ExceptionHandler(MultipartException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ErrorObject handleMultiPartException(
            MultipartException e) 
        //process error message
        return new ErrorObject(e);
    

【讨论】:

请求在尝试控制器之前被拒绝,因此不会使用 controllerAdvice【参考方案3】:

我没有成功通过 HandleException (MultipartException) 捕获错误,而是通过将 MultipartFile 设置为可选(必需 = false)然后检查 MultipartFile 变量是否为 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);
   
   ...

【讨论】:

以上是关于处理没有参数的“multipart/form-data”请求异常的主要内容,如果未能解决你的问题,请参考以下文章

带文件的表单提交

文件上传和下载

tornado之文件上传的几种形式form,伪ajax(iframe)

如何上传文件数组?

Gin框架之文件上传

python: html