18

Tôi gặp sự cố khi bắt và xử lý duyên dáng tệp tải lên của commons FileUploadBase.SizeLimitExceededException hoặc số MaxUploadSizeExceededException của mùa xuân khi tải lên tệp lớn.Sử dụng Spring 3 @ExceptionHandler với commons FileUpload và SizeLimitExceededException/MaxUploadSizeExceededException

Từ những gì tôi có thể nói những trường hợp ngoại lệ này được ném trong quá trình ràng buộc dữ liệu, trước khi bộ điều khiển thực sự đạt được, do đó dẫn đến 500 và không gọi phương thức xử lý ngoại lệ. Có ai đi qua điều này trước đây, và cách tốt nhất để xử lý những ngoại lệ này là đúng cách?

Trả lời

7

nhờ vào thetoolman cho giải pháp đơn giản này. Tôi mở rộng nó một chút. Tôi muốn để các tập tin xử lý không bị ảnh hưởng và vận chuyển Ngoại lệ đến Controller.

package myCompany; 

public class DropOversizeFilesMultipartResolver extends CommonsMultipartResolver { 

    /** 
    * Parse the given servlet request, resolving its multipart elements. 
    * 
    * Thanks Alexander Semenov @ http://forum.springsource.org/showthread.php?62586 
    * 
    * @param request 
    *   the request to parse 
    * @return the parsing result 
    */ 
    @Override 
    protected MultipartParsingResult parseRequest(final HttpServletRequest request) { 

     String encoding = determineEncoding(request); 
     FileUpload fileUpload = prepareFileUpload(encoding); 

     List fileItems; 

     try { 
      fileItems = ((ServletFileUpload) fileUpload).parseRequest(request); 
     } catch (FileUploadBase.SizeLimitExceededException ex) { 
      request.setAttribute(EXCEPTION_KEY, ex); 
      fileItems = Collections.EMPTY_LIST; 
     } catch (FileUploadException ex) { 
      throw new MultipartException("Could not parse multipart servlet request", ex); 
     } 

     return parseFileItems(fileItems, encoding); 
    } 
} 

và trong bộ điều khiển

@InitBinder("fileForm") 
    protected void initBinderDesignForm(WebDataBinder binder) { 
    binder.setValidator(new FileFormValidator()); 
    } 

    @RequestMapping(value = "/my/mapping", method = RequestMethod.POST) 
    public ModelAndView acceptFile(HttpServletRequest request, Model model, FormData formData, 
     BindingResult result) { 

    Object exception = request.getAttribute(DropOversizeFilesMultipartResolver.EXCEPTION_KEY); 
    if (exception != null && FileUploadBase.SizeLimitExceededException.class.equals(exception.getClass())) { 
     result.rejectValue("file", "<your.message.key>"); 
     LOGGER.error(exception); 
    } 

cấu hình mùa xuân vẫn giữ nguyên. Nó sẽ thực sự tốt đẹp nếu có ngoại lệ được chuyển đến người xác nhận, nhưng tôi chưa tìm ra cách thực hiện điều này.

+0

Lưu ý rằng bằng cách đặt 'fileItems = Collections.EMPTY_LIST;', tất cả các tham số yêu cầu đều bị loại bỏ. Nói cách khác, 'request.getParameterMap()' sẽ là '{}'. –

+2

Nhận xét của @Markus đặc biệt quan trọng cần lưu ý nếu một trong các tham số yêu cầu là mã thông báo csrf. Đặt 'fileItems = Collections.EMPTY_LIST;' loại bỏ mã thông báo CSRF, khiến bộ lọc CSRF xử lý yêu cầu này là không hợp lệ. –

1

Điều này có vẻ là một vấn đề khá phổ biến. Tôi đã gặp vấn đề tương tự và các câu hỏi tương tự đã được hỏi, xem ví dụ this question. Tôi chưa thấy một giải pháp tốt cho vấn đề này. Bạn có thể sử dụng một bộ lọc vanilla servlet để xử lý các ngoại lệ này, nhưng điều đó sẽ lặp lại việc xử lý lỗi của bạn vì bạn đã có ExceptionHandler.

+0

Vâng, câu hỏi bạn đã liên kết là một trong số nhiều câu hỏi mà tôi gặp phải trong nghiên cứu của mình, nơi câu hỏi được đặt ra nhưng không tìm thấy giải pháp sạch nào. Tôi đã hy vọng có một cách đẹp hơn, nhưng tôi đồng ý rằng một bộ lọc hoặc thậm chí một trình bao bọc cho trình phân giải có thể là cách duy nhất. – Luke

4

Tôi biết điều này là cũ, nhưng tôi đã tìm kiếm một giải pháp cho điều này là tốt và không thể tìm thấy bất cứ điều gì. Chúng tôi đang cung cấp các dịch vụ RESTful bằng Spring và chúng tôi đang thực hiện tải lên tệp và không chắc chắn cách xử lý việc này. Tôi đã đưa ra sau và hy vọng nó sẽ có ích cho ai đó:

Tất cả các trường hợp ngoại lệ của chúng tôi được xử lý với các chú thích, vì vậy chúng tôi có giải quyết xử lý lỗi của chúng tôi thiết lập như thế này:

@Configuration 
public class MyConfig{ 

    @Bean 
    public AnnotationMethodHandlerExceptionResolver exceptionResolver(){ 

     final AnnotationMethodHandlerExceptionResolver resolver = new AnnotationMethodHandlerExceptionResolver(); 
     resolver.setMessageConverters(messageConverters()); 
     resolver; 
    } 
} 

Sau đó, một chung lớp có thể xử lý các ngoại lệ

public class MultipartExceptionHandler 
{ 

    @ExceptionHandler(MaxUploadSizeExceededException.class) 
    @ResponseStatus(value = HttpStatus.PRECONDITION_FAILED) 
    @ResponseBody 
    protected CustomError handleMaxUploadSizeExceededException(final HttpServletRequest request, 
      final HttpServletResponse response, final Throwable e) 
      throws IOException 
    { 
     logger.error(e); 
     CustomError c = new CustomErrorMaxFileSize("Max file size exceeded", MAX_FILE_SIZE); 
     return c; 
    } 

    @ExceptionHandler(MultipartException.class) 
    @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR) 
    @ResponseBody 
    protected CustomError handleGenericMultipartException(final HttpServletRequest request, 
      final HttpServletResponse response, final Throwable e) 
      throws IOException 
    { 
     logger.error(e); 
     CustomError c = new CustomErrorGeneric("There was a problem with the upload"); 
     return c; 
    } 
} 

Sau đó chúng tôi phân lớp các commons nhiều phần dữ liệu thống xử lý và thực hiện các giao diện HandlerExceptionResolver

@Component(value="multipartResolver") // Spring expects this name 
public class MyMultipartResolver extends CommonsMultipartResolver implements HandlerExceptionResolver 
{ 

    // This is the Spring bean that handles exceptions 
    // We defined this in the Java configuration file 
    @Resource(name = "exceptionResolver") 
    private AnnotationMethodHandlerExceptionResolver exceptionResolver; 

    // The multipart exception handler with the @ExceptionHandler annotation 
    private final MultipartExceptionHandler multipartExceptionHandler = new MultipartExceptionHandler(); 

    // Spring will call this when there is an exception thrown from this 
    // multipart resolver 
    @Override 
    public ModelAndView resolveException(
      final HttpServletRequest request, 
      final HttpServletResponse response, 
      final Object handlerParam, 
      final Exception ex) 
    { 

     // Notice that we pass this.multipartExceptionHandler 
     // and not the method parameter 'handlerParam' into the 
     // exceptionResolver. We do this because the DispatcherServlet 
     // doDispatch() method calls checkMultipart() before determining 
     // the handler for the request. If doing the multipart check fails 
     // with a MultipartException, Spring will never have a reference 
     // to the handler and so 'handlerParam' will be null at this point. 
     return exceptionResolver.resolveException(request, response, this.multipartExceptionHandler, ex); 

    } 
}