MaxUploadSizeExceededException не вызывает метод обработки исключений в Spring

Я использую Spring 3.2.0. В соответствии с ответом this у меня есть тот же метод в моем аннотированном контроллере, который реализует интерфейс HandlerExceptionResolver, такой как

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");
}

и конфигурация Spring включает в себя

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

При превышении размера файла необходимо вызвать предыдущий метод, и он должен автоматически обрабатывать исключение, но этого не происходит вообще. Метод resolveException() никогда не вызывается, даже если это исключение. Каков способ справиться с этим исключением? Я что-то пропустил?

То же самое также указано здесь. Я не уверен, почему это не работает в моем случае.


Я попробовал следующий подход с @ControllerAdvice но это тоже не сработало.

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);
    }
}

Я также попытался поставить подробный - Exception.

@ExceptionHandler(value={Exception.class})

Метод ResponseEntity() никогда не вызывается в любом случае.


В общем, я хочу обработать это исключение на базе контроллера (уровень контроллера), если это возможно. Для этого один аннотированный метод @ExceptionHandler должен быть активен только для этого конкретного контроллера, но не для всего приложения для всего приложения, так как в моем приложении есть только несколько веб-страниц, которые обрабатывают загрузку файлов. Когда это исключение вызвано, я просто хочу показать удобное для пользователя сообщение об ошибке на текущей странице и не перенаправлять на страницу с ошибкой, настроенную в файле web.xml. Если это даже невозможно, то это исключение должно обрабатываться в любом случае без каких-либо особых требований, которые я только что выразил.

Ни один из подходов не работал у меня. Я ничего не могу узнать об обработке этого исключения. Требуется ли дополнительная настройка где-либо в файлах XML или иначе?


То, что я получаю после того, как выбрано исключение, можно увидеть в следующем snap shot.

enter image description here

Ответ 1

В соответствии с вашей опубликованной stacktrace исключение MaxUploadSizeExceeded выдается до того, как запрос достиг сервлета диспетчера. Поэтому ваш обработчик исключений не вызывается, поскольку в момент, когда исключение выбрано, целевой контроллер еще не определен.

Если вы посмотрите на stacktrace, вы увидите, что исключение выбрано в HiddenHttpMethodFilter, которое получает все параметры вашего многостраничного запроса, а также ваш "большой" параметр upload-data.

Требуется ли HiddenHttpMethodFilter для вашего контроллера, обрабатывающего многостраничную загрузку? Если нет, исключите этот фильтр из контроллеров загрузки.

Ответ 2

Из ответа, опубликованного Dirk Lachowski, я исключил некоторые из страниц, которые используются для многостраничной загрузки из HiddenHttpMethodFilter.

HiddenHttpMethodFilter изначально был задан шаблон URL, например /*. Поэтому было утомительно перемещать эти страницы в отдельный каталог/папку и указать другой URL-адрес, например /xxx/*. Чтобы этого избежать, я унаследовал OncePerRequestFilter в своем классе и исключил эти страницы, используемые для многостраничной загрузки, которая работала как ожидалось, показывая пользователю -подробное сообщение об ошибке на текущей странице.

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;
        }
    }
}

И в моем web.xml файле этот фильтр - filter.HiddenHttpMethodFilter был указан вместо org.springframework.web.filter.HiddenHttpMethodFilter следующим образом.

<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>

Я все еще надеюсь, что должен/должен быть справедливый способ справиться с рассматриваемым исключением, а также org.springframework.web.filter.HiddenHttpMethodFilter

Ответ 3

Вы можете настроить свойство resolveLazily для CommonsMultipartResolver следующим образом:

<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="resolveLazily" value="true"/>
</bean>

Ответ 4

Ваш @ExcpetionHandler не работает, потому что эти аннотированные методы позволяют возвращать типы ModelAndView или String из того, что я помню. Подробнее см. эту публикацию.

Ответ 5

Мое решение: сначала определите bean для класса, реализующего HandlerExceptionResolver.

 <bean id="classForBeanException" class="XXXX.path.To.classForBeanException" />

Ответ 6

В вашем ControllerAdvice, который обрабатывает Exception, вы можете иметь такой код, как this.It работал у меня. Это находится в 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;
}