gpt4 book ai didi

java - 如何从 ConstraintViolationException 获取查询参数名称

转载 作者:行者123 更新时间:2023-11-29 04:08:46 27 4
gpt4 key购买 nike

我有一个服务方法:

 @GetMapping(path = "/api/some/path", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<?> getWhatever(@RequestParam(value = "page-number", defaultValue = "0") @Min(0) Integer pageNumber, ...

如果 API 的调用者没有为 page-number 查询参数提交正确的值,则会引发 javax.ConstraintViolationexception。异常消息将如下所示:

getWhatever.pageNumber 必须等于或大于 0

在响应正文中,我想改为使用此消息:

页码必须等于或大于 0

我希望我的消息具有查询参数的名称,而不是参数的名称。恕我直言,包括参数名称在内公开了实现细节。

问题是,我找不到带有查询参数名称的对象。似乎 ConstraintViolationException 没有。

我在 spring-boot 中运行我的应用程序。

如有任何帮助,我们将不胜感激。

P.S.: 我去过其他声称可以解决问题的类似线程,但实际上没有一个真正解决过。

最佳答案

以下是我如何让它在 spring-boot 2.0.3 中工作:

我必须在 spring-boot 中覆盖和禁用 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。

关于java - 如何从 ConstraintViolationException 获取查询参数名称,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56467824/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com