1. 概述
Message interpolation,直译为”消息插值“。是Java Bean验证参数约束并创建错误提示消息的过程。
本文我们将学习如何使用 Spring 默认的插值器来格式化错误消息,以及如何实现自己的插值器。
2. 默认Message Interpolation
开始之前,我们先看一下Spring默认的HTTP 400响应示例,其中违反了 @NotNull
约束:
{
....
"status": 400,
"error": "Bad Request",
"errors": [
{
....
"defaultMessage": "must not be null",
....
}
],
"message": "Validation failed for object='notNullRequest'. Error count: 1",
....
}
每个约束都具有一个 message
字段用于描述错误,Spring从中读取违法约束的错误详情,我们可以自定义,否则使用系统默认生成的。
例如下面的例子,我们创建一个POST
接口:
@RestController
public class RestExample {
@PostMapping("/test-not-null")
public void testNotNull(@Valid @RequestBody NotNullRequest request) {
// ...
}
}
并对请求参数NotNullRequest
的String
字段添加@NotNull
约束:
public class NotNullRequest {
@NotNull(message = "stringValue不能为空")
private String stringValue;
// getters, setters
}
当POST请求为空时时,我们将看到我们的自定义错误消息:
{
...
"errors": [
{
...
"defaultMessage": "stringValue不能为空",
...
}
],
...
}
唯一变化的是defaultMessage
。但我们仍然会得到很多关于错误代码、对象名称、字段名称等的信息。要限制显示的值的数量,我们可以实现REST API的自定义错误消息处理。
3. 使用表达式格式化消息
Spring 支持 使用统一表达语言来定义消息描述,包括 使用逻辑条件表达式,等高级格式功能。
为了更清楚地理解,让我们看几个例子。
在每个约束注解中,我们可以读取正在验证的字段的实际输入的值:
@Size(
min = 5,
max = 14,
message = "作者邮箱 '${validatedValue}' 必须介于 {min} 到 {max} 个字符之间"
)
private String authorEmail;
上面的错误消息模板中读取了参数的实际输入值以及@Size
注解的min
和max
参数:
"defaultMessage": "作者邮箱 'toolongemail@baeldung.com' 必须介于 5 到 14 个字符之间"
注意,对于访问外部变量,我们使用${}
语法,而对于访问验证注解中的其他属性,我们使用{}
符号。
三元运算符也是支持的:
@Min(
value = 1,
message = "There must be at least {value} test{value > 1 ? 's' : ''} in the test case"
)
private int testCount;
输出结果:
"defaultMessage": "There must be at least 2 tests in the test case"
我们还可以调用外部变量上的方法:
@DecimalMin(
value = "50",
message = "The code coverage ${formatter.format('%1$.2f', validatedValue)} must be higher than {value}%"
)
private double codeCoverage;
上面的消息格式化结果示例:
"defaultMessage": "The code coverage 44.44 must be higher than 50%"
如果消息中本身包含了 {、}、$ 等特殊字符,需要先使用发斜杠进行转义。如:{, }, $, \。
4. 自定插值器
在某些情况下,我们想要实现自定义的消息插值引擎,以更好的控制错误输出。首先我们需要实现jakarta.validation.MessageInterpolator
接口:
public class MyMessageInterpolator implements MessageInterpolator {
private final MessageInterpolator defaultInterpolator;
public MyMessageInterpolator(MessageInterpolator interpolator) {
this.defaultInterpolator = interpolator;
}
@Override
public String interpolate(String messageTemplate, Context context) {
messageTemplate = messageTemplate.toUpperCase();
return defaultInterpolator.interpolate(messageTemplate, context);
}
@Override
public String interpolate(String messageTemplate, Context context, Locale locale) {
messageTemplate = messageTemplate.toUpperCase();
return defaultInterpolator.interpolate(messageTemplate, context, locale);
}
}
这个例子很简单,我们只是将错误消息改为大写字母。这样,我们的错误消息将看起来像:
"defaultMessage": "THE CODE COVERAGE 44.44 MUST BE HIGHER THAN 50%"
然后,我们还需要在javax.validation.Validation
工厂中注册我们的插值器:
Validation.byDefaultProvider().configure().messageInterpolator(
new MyMessageInterpolator(
Validation.byDefaultProvider().configure().getDefaultMessageInterpolator())
);
5. 总结
在这篇文章中,我们了解了默认的Spring消息插值的工作原理,以及如何创建自定义的消息插值引擎。
如往常一样,所有的源代码都可以在GitHub上找到。