将错误代码解析为错误消息 (Resolving Error Codes to Error Messages)
我们已经讨论了数据绑定和校验。本节将介绍如何输出与校验错误相对应的消息。
在前一节显示的示例中,我们拒绝了 name 和 age 字段。如果我们想使用 MessageSource 输出错误消息,可以使用我们在拒绝字段时提供的错误代码(在本例中为 'name' 和 'age')。
当你(通过直接或间接地使用 ValidationUtils 类等方式)调用 Errors 接口中的 rejectValue 或其他 reject 方法时,底层实现不仅会注册你传入的代码,还会注册一系列额外的错误代码。MessageCodesResolver 决定了 Errors 接口注册哪些错误代码。默认情况下使用 DefaultMessageCodesResolver,它不仅注册你提供的代码,还会注册包含你传递给 reject 方法的字段名的消息代码。
例如,如果你使用 rejectValue("age", "too.darn.old") 拒绝了一个字段,除了 too.darn.old 代码外,Spring 还会注册 too.darn.old.age 和 too.darn.old.age.int(前者包含字段名,后者包含字段类型)。这样做是为了方便开发人员在精确定位错误消息时提供帮助。
有关 MessageCodesResolver 和默认策略的更多信息,可以分别在 MessageCodesResolver 和 DefaultMessageCodesResolver 的 JavaDoc 中找到。
补充教学
1. 错误代码的层级结构(优先级)
DefaultMessageCodesResolver 会根据特定的策略生成多个错误代码,其顺序通常是从 “最精确” 到 “最通用”。 以 rejectValue("age", "too.young", "Person") 为例,生成的代码列表及其匹配优先级通常如下:
- 代码.对象名.字段名:
too.young.person.age(最精确,针对特定对象的特定字段) - 代码.字段名:
too.young.age(针对所有对象的 age 字段) - 代码.字段类型:
too.young.int(针对所有 int 类型的字段) - 代码:
too.young(最通用)
通过这种机制,你可以为特定的表单字段定义非常精准的提示,也可以为全应用通用的校验逻辑定义统一的提示。
2. 在消息配置文件中定义
你需要在 messages.properties(或关联的语言包文件)中定义这些代码:
# 最精确的定义
too.young.person.age=抱歉,{0} 岁对于注册 Person 来说太年轻了。
# 通用的定义
too.young=年龄不符合要求。3. 在 Spring Boot 中的应用
在现代 Spring Boot 应用程序中,你几乎不需要手动配置 MessageSource。
- 自动配置:Spring Boot 会自动查找
src/main/resources下的messages.properties文件。 - i18n 支持:通过
Accept-Language请求头,Spring 会自动切换到对应的语言文件(如messages_en.properties,messages_zh_CN.properties)。 - 前端回显:如果你使用 Thymeleaf,可以使用
#{...}语法配合th:errors自动显示这些解析后的消息。
4. 为什么会有类型后缀(如 .int)?
这是为了处理类型绑定失败的情况。例如,当用户在数字输入框输入了字符串 "abc" 时,Spring 的 DataBinder 会自动触发一个 typeMismatch 错误。 解析器会生成如 typeMismatch.age 和 typeMismatch.int 的代码。你只需要由于配置文件中定义: typeMismatch.int=请输入合法的数字类型。 这样,即便你没有编写任何校验逻辑,用户也能看到友好的类型提示。