You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot带国际化的自定义验证消息实现及问题排查

Spring Boot 验证注解的动态消息与国际化实现问题

问题背景

我正在为验证注解寻找合适的实现方式,现有示例代码如下:

public class UserRequest {

    @Size(min = 3, max = 50)
    @NotBlank(message = "name cannot be empty and min 3, max 50 character length")
    private String name;

    @Email(message = "email is not valid")
    private String email;
}

需要实现以下特性:

  • 像name字段的验证消息一样,能够在消息中动态获取字段名、min和max等参数值;
  • 未来可支持国际化。

请问在Spring Boot应用中该如何实现?MessageSource方案是否可行?

更新:尝试后的问题

我尝试MessageSource方案后,得到如下响应,看起来resources目录下的message.properties中的消息并未被解析:

{
    "timestamp": "26.02.2023 01:10:15",
    "status": 400,
    "error": "Bad Request",
    "exception": "org.springframework.web.bind.MethodArgumentNotValidException",
    "message": "Validation failed for object='createPostRequest'. Error count: 1",
    "errors": [
        {
            "codes": [
                "NotBlank.createPostRequest.name",
                "NotBlank.name",
                "NotBlank.java.lang.String",
                "NotBlank"
            ],
            "arguments": [
                {
                    "codes": [
                        "createPostRequest.name",
                        "name"
                    ],
                    "arguments": null,
                    "defaultMessage": "name",
                    "code": "name"
                }
            ],
            "defaultMessage": "{name.not-blank}",
            "objectName": "createPostRequest",
            "field": "name",
            "rejectedValue": null,
            "bindingFailure": false,
            "code": "NotBlank"
        }
    ],
    "path": "/api/v1/posts"
}

解决方案

一、MessageSource方案完全可行

Spring Boot自带的MessageSource结合JSR-380(Bean Validation)的消息插值机制,完全能满足动态获取字段名、验证参数及国际化的需求。

二、完整实现步骤

1. 配置消息资源文件

在resources目录下创建对应语言的消息文件,Spring Boot会自动加载:

  • 中文默认消息:messages.properties
  • 英文消息:messages_en.properties

以messages.properties为例,添加验证规则对应的消息模板:

# NotBlank验证:{0}会被替换为字段名
NotBlank={0}不能为空
# Size验证:{0}=字段名,{1}=min值,{2}=max值
Size={0}长度必须在{1}到{2}之间
# Email验证
Email={0}格式不合法

2. 修改实体类的验证注解

不要在注解message属性中写死文本,改为引用消息文件中的key:

public class UserRequest {

    @Size(min = 3, max = 50, message = "{Size}")
    @NotBlank(message = "{NotBlank}")
    private String name;

    @Email(message = "{Email}")
    private String email;
}

3. 全局异常处理器解析消息

Spring Boot抛出MethodArgumentNotValidException时,需通过MessageSource解析消息参数,返回友好响应:

@RestControllerAdvice
public class GlobalExceptionHandler {

    private final MessageSource messageSource;

    public GlobalExceptionHandler(MessageSource messageSource) {
        this.messageSource = messageSource;
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<Map<String, Object>> handleValidationException(MethodArgumentNotValidException ex, Locale locale) {
        List<String> errors = ex.getBindingResult()
                .getFieldErrors()
                .stream()
                .map(error -> messageSource.getMessage(error, locale))
                .collect(Collectors.toList());

        Map<String, Object> response = new HashMap<>();
        response.put("timestamp", LocalDateTime.now().format(DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm:ss")));
        response.put("status", HttpStatus.BAD_REQUEST.value());
        response.put("error", "参数错误");
        response.put("message", "参数验证失败");
        response.put("errors", errors);
        response.put("path", ex.getRequest().getRequestURI());

        return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST);
    }
}

4. 解决消息未解析的问题

从你的响应来看,消息模板未被解析,大概率是以下原因:

  • 消息key不匹配:你用了{name.not-blank},但消息文件中无对应key,或拼写错误;
  • MessageSource配置错误:如果自定义了MessageSource,需确保basename设置正确(比如classpath:messages)、默认编码为UTF-8;
  • 注解message属性错误:实体类中@NotBlank的message应引用全局key(如"{NotBlank}"),而非自定义的"{name.not-blank}";
  • Locale不匹配:确保请求Locale与消息文件对应,或设置默认Locale。

若需自定义MessageSource,参考以下配置:

@Configuration
public class MessageConfig {

    @Bean
    public MessageSource messageSource() {
        ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
        messageSource.setBasename("classpath:messages");
        messageSource.setDefaultEncoding("UTF-8");
        messageSource.setCacheSeconds(3600); // 开发环境可设为0
        return messageSource;
    }

    @Bean
    public LocalValidatorFactoryBean validator() {
        LocalValidatorFactoryBean bean = new LocalValidatorFactoryBean();
        bean.setValidationMessageSource(messageSource());
        return bean;
    }
}

三、验证效果

当name字段为空时,返回消息:"name不能为空";当name长度不符合要求时,返回"name长度必须在3到50之间",完全满足动态参数和国际化需求。


内容的提问来源于stack exchange,提问作者Jack

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 04:27:17