Gre*_*een 9 java validation error-handling spring-boot
我正在使用 Spring Boot 开发一个 API,目前我正在考虑如何以一种易于国际化的方式处理错误消息。我的目标如下:
@Length
以声明方式将约束注释与错误消息(例如,)连接起来{min}
,它们被注释中的相应值替换(如果可用),例如,@Length(min = 5, message = msg)
将导致类似 的结果msg.replace("{min}", annotation.min()).replace("{max}", annotation.max())
。目前,我自定义了methodArgumentNotValidHandler
错误处理程序类的读取内容ObjectError
,e.getBindingResult().getAllErrors()
然后尝试提取它们的参数和错误代码,以决定从资源包中选择哪个错误消息并相应地对其进行格式化。我的代码的粗略草图如下所示:
输入:
@Data
@RequiredArgsConstructor
public class RequestBody {
@NotNull
@NotBlank(message = ErrorConstants.NOT_BLANK)
@Length(min = 5, max = 255, message = ErrorConstants.LENGTH_MIN_MAX) // LENGTH_MIN_MAX = validation.length.min-max
private String greeting;
}
Run Code Online (Sandbox Code Playgroud)
错误处理程序:
@ResponseBody
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
ErrorMessage methodArgumentNotValidHandler(MethodArgumentNotValidException e) {
ObjectError objectError = e.getBindingResult().getAllErrors().get(0);
Object[] arguments = objectError.getArguments();
String messageCode = objectError.getDefaultMessage(); // e.g., "validation.length.min-max" (key in resource bundle)
ResourceBundle errMsgBundle = ResourceBundle.getBundle("errorMsg");
String message;
if (objectError.getCode().equals("Length")) {
String messageTemplate = errMsgBundle.getString(messageCode);
message = String.format(messageTemplate, arguments[2], arguments[1]);
} else {
message = "Bad input, but I cannot tell you the problem because the programmer hasn't handled this yet. Sorry :'(";
}
return new ErrorMessage(message);
}
Run Code Online (Sandbox Code Playgroud)
不幸的是,我认为这种方法不可维护。在错误处理程序中,我最终会得到一个巨大的 if-else 块,它必须探测几种不同的情况(错误代码、参数数量……)并相应地格式化错误消息。更改错误消息可能会导致必须更改代码(例如,参数的顺序)。每个属性键必须作为常量存在ErrorConstants
,我认为这是不可取的。此代码也不查询错误属性的名称或路径,例如“name”。
因此,
经过一番挖掘后,我发现我正在寻找的东西确实已经内置了,因为我希望每个想要很好地表现自己的开发人员都会问这个问题。事实上,这个问题已经被问过(如果我能正确表达我的要求,我本可以更早找到它)。我只是要求通过资源包自定义我的本地化错误消息。
当我在包含自定义错误消息的资源文件夹中创建资源包并将其命名为“validation_errors.properties”时,我可以通过创建相应的 bean 来使验证器使用这些消息:
@Bean
public Validator validatorFactory (MessageSource messageSource) {
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
validator.setValidationMessageSource(messageSource);
return validator;
}
@Bean
public MessageSource messageSource() {
ReloadableResourceBundleMessageSource bean = new ReloadableResourceBundleMessageSource();
bean.addBasenames("classpath:org.hibernate.validator.ValidationMessages", "classpath:validation_errors"); // validation_errors.properties is my resource bundle
bean.setDefaultEncoding("UTF-8");
return bean;
}
Run Code Online (Sandbox Code Playgroud)
我的自定义验证器从 的实例检索验证消息ReloadableResourceBundleMessageSource
,该实例又从属性文件中检索它们。
属性文件包含验证注释的“消息”参数的限定路径作为键和值任意字符串,其中大括号中的字符串被验证注释中的参数替换,并且计算SpEL 表达式。
javax.validation.constraints.NotNull.message = Not null please!
javax.validation.constraints.NotBlank.message = Not empty please!
org.hibernate.validator.constraints.Length.message = String length between {min} and {max} please!
Run Code Online (Sandbox Code Playgroud)
接下来,在我的错误处理程序中,我需要检测并解包ObjectError
中的实例是否MethodArgumentNotValidException
包含 a ConstraintViolation
(为了简化此示例,我忽略其他错误源):
@ResponseBody
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
List<ErrorMessage> methodArgumentNotValidHandler(MethodArgumentNotValidException e) {
return e.getBindingResult().getAllErrors().stream()
.filter(objectError -> objectError.contains(ConstraintViolation.class))
.map(objectError -> objectError.unwrap(ConstraintViolation.class))
.map(ConstraintViolation::getMessage)
.map(message -> new ErrorMessage("VE-400", message))
.collect(Collectors.toList());
}
Run Code Online (Sandbox Code Playgroud)
该解决方案满足要求 1、3、5 和 6。要求 2 被视为无效,因为它与我提出此问题时想到的特定解决方案相关。要求 4 仍然开放,SpEL 可能有可能进一步研究,否则我将继续探索Tris 答案。
归档时间: |
|
查看次数: |
12497 次 |
最近记录: |