MaxUploadSizeExceededException doesn't invoke the exception handling method in Spring
Asked Answered
A

7

14

I'm using Spring 3.2.0. According to this answer, I have the same method in my annotated controller which implements the HandlerExceptionResolver interface such as,

public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception exception) {

    Map<String, Object> model = new HashMap<String, Object>(0);

    if (exception instanceof MaxUploadSizeExceededException) {
        model.put("msg", exception.toString());
        model.put("status", "-1");
    } else {
        model.put("msg", "Unexpected error : " + exception.toString());
        model.put("status", "-1");
    }

    return new ModelAndView("admin_side/ProductImage");
}

and the Spring configuration includes,

<bean id="filterMultipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <property name="maxUploadSize">
        <value>10000</value>
    </property>
</bean>

When the file size exceeds, the preceding method should be invoked and it should automatically handle the exception but it doesn't happen at all. The method resolveException() is never called even though the exception occurs. What is the way to handle this exception? Am I missing something?

The same thing is also specified here. I'm not sure why it doesn't work in my case.


I have tried the following approach with @ControllerAdvice but it didn't work either.

package exceptionhandler;

import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

@ControllerAdvice
public final class RestResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {

    @ExceptionHandler(value = {MaxUploadSizeExceededException.class})
    protected ResponseEntity<Object> handleConflict(RuntimeException ex, WebRequest request) {
        String bodyOfResponse = "This should be application specific";
        return handleExceptionInternal(ex, bodyOfResponse, new HttpHeaders(), HttpStatus.CONFLICT, request);
    }
}

I have also tried to put the verbose - Exception.

@ExceptionHandler(value={Exception.class})

The method ResponseEntity() is never invoked in any case.


In general, I want to handle this exception per controller base (controller level), if possible. For this, one @ExceptionHandler annotated method should only be active for that particular controller, not globally for the entire application, since there are only a few web pages in my application that handle file uploading. When this exception is caused, I just want to show a user-friendly error message on the current page and not to redirect to the error page configured in the web.xml file. If this is not even feasible, then this exception should be handled anyway without any custom requirements I have just expressed.

Neither of approaches worked for me. Nothing more about handling this exception I could find. Does it require additional configuration somewhere in XML files or otherwise?


What I'm getting after the exception is thrown can be visible in the following snap shot.

enter image description here

Anodic answered 12/1, 2013 at 21:47 Comment(4)
It's not clear if you tried that, but @ExceptionHandler only catches exceptions thrown from the Controller it's defined in, not globally (ie. "I want to handle this exception per controller" is essentially the only option)Couture
@Couture : The method annotated with @ExceptionHandler is never invoked in any case, neither at controller level. It is invoked with other exceptions but not with MaxUploadSizeExceededException.Anodic
did you resolve this?Raouf
@Raouf : Yes as specified in the accepted answer.Anodic
T
8

According to your posted stacktrace the MaxUploadSizeExceeded exception is thrown before the request has reached the dispatcher servlet. Therefore your exceptionhandler isn't called because at the point the exception is thrown the target controller has yet to be determined.

If you look at the stacktrace you can see that the exception is thrown in the HiddenHttpMethodFilter that gets all parameters of your multipart-request - and also your "to big" upload-data parameter.

Is the HiddenHttpMethodFilter needed for your controller handling the multipart-upload? If not, exclude this filter from your upload-handling controllers.

Tracheid answered 9/4, 2013 at 15:5 Comment(1)
That has been done! Many thanks for identifying the exact problem domain.Anodic
G
3

You can config the CommonsMultipartResolver's resolveLazily property to true like this:

<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="resolveLazily" value="true"/>
</bean>
Greave answered 21/8, 2013 at 9:14 Comment(4)
This was my thread on the official Spring forum. No luck after doing this!Anodic
try to use @HttpExceptionHandlerGreave
@jetma where is the annotation @HttpExceptionHandler?Coggins
@janwen sorry, it's @ExceptionHandlerGreave
A
2

From the answer posted by Dirk Lachowski, I have excluded some of the pages which are used for multipart-upload from HiddenHttpMethodFilter.

HiddenHttpMethodFilter was originally given a URL pattern like /*. Therefore, it was tedious to move these pages in a separate directory/folder and specify a different URL patter like /xxx/*. To avoid this, I have inherited OncePerRequestFilter in my own class and excluded these pages used for multipart-upload that worked as expected showing a user-friendly error message on the current page.

package filter;

import java.io.IOException;
import java.util.Locale;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

public final class HiddenHttpMethodFilter extends OncePerRequestFilter {

    /**
     * Default method parameter: <code>_method</code>
     */
    public static final String DEFAULT_METHOD_PARAM = "_method";

    private String methodParam = DEFAULT_METHOD_PARAM;

    /**
     * Set the parameter name to look for HTTP methods.
     *
     * @see #DEFAULT_METHOD_PARAM
     */
    public void setMethodParam(String methodParam) {
        Assert.hasText(methodParam, "'methodParam' must not be empty");
        this.methodParam = methodParam;
    }

    private boolean excludePages(String page) {
        //Specifically, in my case, this many pages so far have been excluded from processing avoiding the MaxUploadSizeExceededException in this filter. One could use a RegExp or something else as per requirements.
        if (page.equalsIgnoreCase("Category.htm") || page.equalsIgnoreCase("SubCategory.htm") || page.equalsIgnoreCase("ProductImage.htm") || page.equalsIgnoreCase("Banner.htm") || page.equalsIgnoreCase("Brand.htm")) {
            return false;
        }
        return true;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {

        String servletPath = request.getServletPath();

        if (excludePages(servletPath.substring(servletPath.lastIndexOf("/") + 1, servletPath.length()))) {
            String paramValue = request.getParameter(this.methodParam);
            //The MaxUploadSizeExceededException was being thrown at the preceding line.
            if ("POST".equals(request.getMethod()) && StringUtils.hasLength(paramValue)) {
                String method = paramValue.toUpperCase(Locale.ENGLISH);
                HttpServletRequest wrapper = new filter.HiddenHttpMethodFilter.HttpMethodRequestWrapper(request, method);
                filterChain.doFilter(wrapper, response);
            } else {
                filterChain.doFilter(request, response);
            }
        } else {
            filterChain.doFilter(request, response);
        }
    }

    /**
     * Simple {@link HttpServletRequest} wrapper that returns the supplied
     * method for {@link HttpServletRequest#getMethod()}.
     */
    private static class HttpMethodRequestWrapper extends HttpServletRequestWrapper {

        private final String method;

        public HttpMethodRequestWrapper(HttpServletRequest request, String method) {
            super(request);
            this.method = method;
        }

        @Override
        public String getMethod() {
            return this.method;
        }
    }
}

And in my web.xml file, this filter - filter.HiddenHttpMethodFilter was specified instead of org.springframework.web.filter.HiddenHttpMethodFilter as follows.

<filter>
    <filter-name>multipartFilter</filter-name>
    <filter-class>org.springframework.web.multipart.support.MultipartFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>multipartFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

<filter>
    <filter-name>httpMethodFilter</filter-name>
    <filter-class>filter.HiddenHttpMethodFilter</filter-class>
    <!--<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class> This was removed replacing with the preceding one-->
</filter>
<filter-mapping>
    <filter-name>httpMethodFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

I'm still hoping that there should/could be a fair way to handle the exception in question along with org.springframework.web.filter.HiddenHttpMethodFilter

Anodic answered 10/4, 2013 at 12:8 Comment(0)
B
1

Your @ExcpetionHandler isn't working because those annotated methods only allow return types of ModelAndView or String, from what I remember. See this posting for further details.

Basipetal answered 5/4, 2013 at 19:46 Comment(4)
That was already given a try with a return type of String also which is interpreted as a view name but unfortunately none of them worked for me.Anodic
Looking at the snap shot in the question body, it can be noticed that the browser doesn't indicate the status 500 because the entire request is rejected and it is not processed.Anodic
The method is only invoked with the exceptions other than the exception in question MaxUploadSizeExceededException.Anodic
I am behind a proxy server, and cannot see the snapshot. Is it possible to put a break on that particular exception to make sure it's actually being thrown?Basipetal
T
1

My solution :First define bean for class that implements HandlerExceptionResolver .

 <bean id="classForBeanException" class="XXXX.path.To.classForBeanException" />
Thorma answered 8/4, 2013 at 9:50 Comment(0)
E
1

In your ControllerAdvice which is handling the Exception you can have code like this.It worked for me.This is in spring 4.0+

@ExceptionHandler(Exception.class)
public @ResponseBody BaseResponse onException(Exception e, HttpServletResponse response) {
    response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);

    BaseResponse resp = new BaseResponse();
    if(e instanceof MaxUploadSizeExceededException){
        resp.setCode(FileUploadFailed.SIZE_EXCEED);
        resp.setMessage("Maximum upload size exceeded");
    }
    return resp;
}
Ellon answered 26/2, 2016 at 8:55 Comment(0)
M
0

Is there anyway to get the @RequestParam arguments defined in the controller method in the ExceptionHandler that manages the MaxUploadSizeExceededException? Seems to be being thrown before getting to the Controller method.

Mccue answered 15/11, 2019 at 14:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.