Skip to content

将错误代码解析为错误消息 (Resolving Error Codes to Error Messages)

我们已经讨论了数据绑定和校验。本节将介绍如何输出与校验错误相对应的消息。

前一节显示的示例中,我们拒绝了 nameage 字段。如果我们想使用 MessageSource 输出错误消息,可以使用我们在拒绝字段时提供的错误代码(在本例中为 'name' 和 'age')。

当你(通过直接或间接地使用 ValidationUtils 类等方式)调用 Errors 接口中的 rejectValue 或其他 reject 方法时,底层实现不仅会注册你传入的代码,还会注册一系列额外的错误代码。MessageCodesResolver 决定了 Errors 接口注册哪些错误代码。默认情况下使用 DefaultMessageCodesResolver,它不仅注册你提供的代码,还会注册包含你传递给 reject 方法的字段名的消息代码。

例如,如果你使用 rejectValue("age", "too.darn.old") 拒绝了一个字段,除了 too.darn.old 代码外,Spring 还会注册 too.darn.old.agetoo.darn.old.age.int(前者包含字段名,后者包含字段类型)。这样做是为了方便开发人员在精确定位错误消息时提供帮助。

有关 MessageCodesResolver 和默认策略的更多信息,可以分别在 MessageCodesResolverDefaultMessageCodesResolver 的 JavaDoc 中找到。


补充教学

1. 错误代码的层级结构(优先级)

DefaultMessageCodesResolver 会根据特定的策略生成多个错误代码,其顺序通常是从 “最精确”“最通用”。 以 rejectValue("age", "too.young", "Person") 为例,生成的代码列表及其匹配优先级通常如下:

  1. 代码.对象名.字段名too.young.person.age(最精确,针对特定对象的特定字段)
  2. 代码.字段名too.young.age(针对所有对象的 age 字段)
  3. 代码.字段类型too.young.int(针对所有 int 类型的字段)
  4. 代码too.young(最通用)

通过这种机制,你可以为特定的表单字段定义非常精准的提示,也可以为全应用通用的校验逻辑定义统一的提示。

2. 在消息配置文件中定义

你需要在 messages.properties(或关联的语言包文件)中定义这些代码:

properties
# 最精确的定义
too.young.person.age=抱歉,{0} 岁对于注册 Person 来说太年轻了。
# 通用的定义
too.young=年龄不符合要求。

3. 在 Spring Boot 中的应用

在现代 Spring Boot 应用程序中,你几乎不需要手动配置 MessageSource

  1. 自动配置:Spring Boot 会自动查找 src/main/resources 下的 messages.properties 文件。
  2. i18n 支持:通过 Accept-Language 请求头,Spring 会自动切换到对应的语言文件(如 messages_en.properties, messages_zh_CN.properties)。
  3. 前端回显:如果你使用 Thymeleaf,可以使用 #{...} 语法配合 th:errors 自动显示这些解析后的消息。

4. 为什么会有类型后缀(如 .int)?

这是为了处理类型绑定失败的情况。例如,当用户在数字输入框输入了字符串 "abc" 时,Spring 的 DataBinder 会自动触发一个 typeMismatch 错误。 解析器会生成如 typeMismatch.agetypeMismatch.int 的代码。你只需要由于配置文件中定义: typeMismatch.int=请输入合法的数字类型。 这样,即便你没有编写任何校验逻辑,用户也能看到友好的类型提示。

Based on Spring Framework.