Почему некоторые параметры метода нужно ставить раньше других?

Собственно столкнулся с проблемой, на решение которой потратил много времени - Нет редиректа после ошибки валидации Spring boot

Поясните почему так и с чем это связано? А то не хочется в следующий раз тратить два дня на решение такой ерунды.


Ответы (1 шт):

Автор решения: Nowhere Man

Подобный вопрос на основном SO: Why does BindingResult have to follow @Valid?

Такой порядок обусловлен тем, что в обработчике запросов контроллера потенциально может быть несколько объектов, для которых требуется валидация, то есть, помеченных аннотацией @Valid. Соответственно, для каждого такого объекта понадобится свой экземпляр BindingResult, и вполне логично, чтобы эти объекты находились рядом.

Сама валидация выполняется в методе org.springframework.web.method.annotation.ModelAttributeMethodProcessor::resolveArgument:

public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
// ....
        if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
            throw new BindException(binder.getBindingResult());
        }
// ...
}

И соответственно в методе isBindExceptionRequired проверяется, что следующий за данным параметром объект НЕ является экземпляром интерфейса Errors, от которого унаследован интерфейс BindingResult, то есть, НУЖНО выбросить соответствующее исключение:

    /**
     * Whether to raise a fatal bind exception on validation errors.
     * @param parameter the method parameter declaration
     * @return {@code true} if the next method parameter is not of type {@link Errors}
     * @since 5.0
     */
    protected boolean isBindExceptionRequired(MethodParameter parameter) {
        int i = parameter.getParameterIndex();
        Class<?>[] paramTypes = parameter.getExecutable().getParameterTypes();
        boolean hasBindingResult = (paramTypes.length > (i + 1) && Errors.class.isAssignableFrom(paramTypes[i + 1]));
        return !hasBindingResult;
    }
→ Ссылка