Вот как я заставил это работать в весенней загрузке 2.0.3:
Мне пришлось переопределить и отключить ValidationAutoConfiguration
в весенней загрузке:
import org.springframework.boot.validation.MessageInterpolatorFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.env.Environment;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
import javax.validation.Validator;
@Configuration
public class ValidationConfiguration {
public ValidationConfiguration() {
}
@Bean
public static LocalValidatorFactoryBean validator() {
LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean();
factoryBean.setParameterNameDiscoverer(new CustomParamNamesDiscoverer());
MessageInterpolatorFactory interpolatorFactory = new MessageInterpolatorFactory();
factoryBean.setMessageInterpolator(interpolatorFactory.getObject());
return factoryBean;
}
@Bean
public static MethodValidationPostProcessor methodValidationPostProcessor(Environment environment, @Lazy Validator validator) {
MethodValidationPostProcessor processor = new MethodValidationPostProcessor();
boolean proxyTargetClass = (Boolean) environment.getProperty("spring.aop.proxy-target-class", Boolean.class, true);
processor.setProxyTargetClass(proxyTargetClass);
processor.setValidator(validator);
return processor;
}
}
CustomParamNamesDiscoverer
находится в том же пакете, и это в значительной степени копирование-вставка DefaultParameterNameDiscoverer
, реализация по умолчанию в Spring-boot для параметра обнаружения имен параметров:
import org.springframework.core.*;
import org.springframework.util.ClassUtils;
public class CustomParameterNameDiscoverer extends PrioritizedParameterNameDiscoverer {
private static final boolean kotlinPresent = ClassUtils.isPresent("kotlin.Unit", CustomParameterNameDiscoverer.class.getClassLoader());
public CustomParameterNameDiscoverer() {
if (kotlinPresent) {
this.addDiscoverer(new KotlinReflectionParameterNameDiscoverer());
}
this.addDiscoverer(new ReqParamNamesDiscoverer());
this.addDiscoverer(new StandardReflectionParameterNameDiscoverer());
this.addDiscoverer(new LocalVariableTableParameterNameDiscoverer());
}
}
Я хотел, чтобы он оставался практически без изменений (вы можетесм. там даже проверки kotlin) с единственным дополнением: я добавляю экземпляр ReqParamNamesDiscoverer
в связанные списки открывателей.Обратите внимание, что порядок добавления здесь имеет значение.
Вот исходный код:
import com.google.common.base.Strings;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.lang.Nullable;
import org.springframework.web.bind.annotation.RequestParam;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
public class ReqParamNamesDiscoverer implements ParameterNameDiscoverer {
public ReqParamNamesDiscoverer() {
}
@Override
@Nullable
public String[] getParameterNames(Method method) {
return doGetParameterNames(method);
}
@Override
@Nullable
public String[] getParameterNames(Constructor<?> constructor) {
return doGetParameterNames(constructor);
}
@Nullable
private static String[] doGetParameterNames(Executable executable) {
Parameter[] parameters = executable.getParameters();
String[] parameterNames = new String[parameters.length];
for (int i = 0; i < parameters.length; ++i) {
Parameter param = parameters[i];
if (!param.isNamePresent()) {
return null;
}
String paramName = param.getName();
if (param.isAnnotationPresent(RequestParam.class)) {
RequestParam requestParamAnnotation = param.getAnnotation(RequestParam.class);
if (!Strings.isNullOrEmpty(requestParamAnnotation.value())) {
paramName = requestParamAnnotation.value();
}
}
parameterNames[i] = paramName;
}
return parameterNames;
}
}
Если параметр помечен аннотацией RequestParam
, я получаю атрибут value
иверните его в качестве имени параметра.
Следующим шагом было отключение конфигурации автоматической проверки, так или иначе, она не работает без нее.Эта аннотация делает свое дело, хотя: @SpringBootApplication(exclude = {ValidationAutoConfiguration.class})
Кроме того, вам необходимо иметь собственный обработчик для ConstraintValidationException
:
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(ConstraintViolationException.class)
public ErrorDTO handleConstraintViolationException(ConstraintViolationException ex) {
Map<String, Collection<String>> errors = new LinkedHashMap<>();
ex.getConstraintViolations().forEach(constraintViolation -> {
String queryParamPath = constraintViolation.getPropertyPath().toString();
log.debug("queryParamPath = {}", queryParamPath);
String queryParam = queryParamPath.contains(".") ?
queryParamPath.substring(queryParamPath.indexOf(".") + 1) :
queryParamPath;
String errorMessage = constraintViolation.getMessage();
Collection<String> perQueryParamErrors = errors.getOrDefault(queryParam, new ArrayList<>());
perQueryParamErrors.add(errorMessage);
errors.put(queryParam, perQueryParamErrors);
});
return validationException(new ValidationException("queryParameter", errors));
}
ValidationException
Материал - это мой собственный способ работыв двух словах, с ошибками проверки, в двух словах выдается ошибка DTO, которая будет сериализована в JSON со всеми сообщениями об ошибках проверки.