【问题标题】:Hibernate Validation does not look up error messages in a Spring boot applicationHibernate Validation 不在 Spring Boot 应用程序中查找错误消息
【发布时间】:2018-12-02 06:56:04
【问题描述】:

这是我设置的 Spring Boot 项目。

我有一个标准的 Spring Boot JPA 堆栈,我试图在其中验证实例变量的状态。我的豆子如下:

配置

@Component
public class ConfigurationHelper {

    @Bean
    public MessageSource messageSource() {
        ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
        messageSource.setBasenames("messages");
        messageSource.setUseCodeAsDefaultMessage(true);
        messageSource.setCacheSeconds(5);
        messageSource.setDefaultEncoding("UTF-8");
        return messageSource;
    }

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

模型类

@Table(name = "user_station", indexes = {
    @Index(columnList = "station_id", name = "station_index_station_id"),
    @Index(columnList = "station_name", name="station_index_name"),
    @Index(columnList = "hd_enabled", name = "station_index_hd_enabled")
})
@Entity
@EqualsAndHashCode(exclude = {"createdTimeStamp", "updatedTimestamp"}, callSuper = true)
@ToString(exclude = {"createdTimeStamp", "updatedTimestamp"}, callSuper = true)
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Station extends IError {

    @Id
    @GeneratedValue(strategy= GenerationType.AUTO)
    @JsonIgnore
    private Long id;

    // Represents a station id for the user.
    @Column(name="station_id", nullable = false, unique = true)
    @NotEmpty(message = "{station.id.empty}")
    @Pattern(regexp = "^K|W[A-Za-z0-9\\-].*$", message = "{station.id.name.not.valid}")
    private String stationId;

    @Column(name="station_name", nullable = false)
    @JsonProperty("name")
    @NotEmpty(message = "{station.name.empty}")
    private String stationName;

    @Column(name = "hd_enabled")
    private Boolean hdEnabled;

    @Column(name="call_sign", nullable = false)
    @NotEmpty(message = "{station.call.sign.empty}")
    private String callSign;

    @Column(name="user_created_timestamp")
    @JsonIgnore
    private LocalDateTime createdTimeStamp;

    @Column(name="user_modified_timestamp")
    @JsonIgnore
    private LocalDateTime updatedTimestamp;

    /**
     * Initialises the timestamps prior to update or insertions.
     *
     * <p>The implementation ensures that time stamps would always reflect the time when entities
     * were persisted or updated.
     */
    @PrePersist
    @PreUpdate
    public void setTimestamps() {
        LocalDateTime utcNow = LocalDateTime.now(ZoneOffset.UTC);
        if (this.createdTimeStamp == null) {
            this.createdTimeStamp = utcNow;
        }
        this.updatedTimestamp = utcNow;
        if (this.hdEnabled == null) {
            this.hdEnabled = Boolean.FALSE;
        }
    }

    // Getters, Setters, Equals and HashCode functions.
}

单元测试

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes= {App.class})
public class StationTest {

    @Autowired
    private Validator validator;

    private Station station;

    @Before
    public void setUp() throws Exception {
        this.station = new Station();
    }


    @Test
    public void testValidator_allNulls() {
        Set<ConstraintViolation<Station>> constraintViolations =
            this.validator.validate(this.station);
        MatcherAssert.assertThat(constraintViolations.isEmpty(), Is.is(false));
        for (ConstraintViolation<Station> constraintViolation : constraintViolations) {
            System.out.println(constraintViolation.getMessage());
        }
    }
}

这是我的输出

. . . . Rest of the stack trace omitted . . . .

     2018-12-01 23:30:38.532  INFO 21297 --- [           main] com.iheartmedia.model.StationTest        : Starting StationTest on Kartiks-MacBook-Pro-2.local with PID 21297 (started by krishnanand in /Users/krishnanand/projects/iheartmedia)
2018-12-01 23:30:38.533 DEBUG 21297 --- [           main] com.iheartmedia.model.StationTest        : Running with Spring Boot v2.0.5.RELEASE, Spring v5.0.9.RELEASE
2018-12-01 23:30:38.539  INFO 21297 --- [           main] com.iheartmedia.model.StationTest        : No active profile set, falling back to default profiles: default
2018-12-01 23:30:38.596  INFO 21297 --- [           main] o.s.w.c.s.GenericWebApplicationContext   : Refreshing org.springframework.web.context.support.GenericWebApplicationContext@6892b3b6: startup date [Sat Dec 01 23:30:38 PST 2018]; root of context hierarchy
2018-12-01 23:30:39.565  INFO 21297 --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration' of type [org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration$$EnhancerBySpringCGLIB$$b7d31eab] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2018-12-01 23:30:39.730  INFO 21297 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2018-12-01 23:30:39.871  INFO 21297 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2018-12-01 23:30:39.903  INFO 21297 --- [           main] j.LocalContainerEntityManagerFactoryBean : Building JPA container EntityManagerFactory for persistence unit 'default'
2018-12-01 23:30:39.917  INFO 21297 --- [           main] o.hibernate.jpa.internal.util.LogHelper  : HHH000204: Processing PersistenceUnitInfo [
    name: default
    ...]
2018-12-01 23:30:40.030  INFO 21297 --- [           main] org.hibernate.Version                    : HHH000412: Hibernate Core {5.2.17.Final}
2018-12-01 23:30:40.031  INFO 21297 --- [           main] org.hibernate.cfg.Environment            : HHH000206: hibernate.properties not found
2018-12-01 23:30:40.066  INFO 21297 --- [           main] o.hibernate.annotations.common.Version   : HCANN000001: Hibernate Commons Annotations {5.0.1.Final}
2018-12-01 23:30:40.196  INFO 21297 --- [           main] org.hibernate.dialect.Dialect            : HHH000400: Using dialect: org.hibernate.dialect.H2Dialect
2018-12-01 23:30:40.652  INFO 21297 --- [           main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2018-12-01 23:30:40.985  INFO 21297 --- [           main] o.h.h.i.QueryTranslatorFactoryInitiator  : HHH000397: Using ASTQueryTranslatorFactory
2018-12-01 23:30:41.351  INFO 21297 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2018-12-01 23:30:41.586  INFO 21297 --- [           main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.web.context.support.GenericWebApplicationContext@6892b3b6: startup date [Sat Dec 01 23:30:38 PST 2018]; root of context hierarchy
2018-12-01 23:30:41.624  WARN 21297 --- [           main] aWebConfiguration$JpaWebMvcConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
2018-12-01 23:30:41.658  INFO 21297 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/iheartmedia/stations],methods=[GET]}" onto public java.util.List<com.iheartmedia.model.Station> com.iheartmedia.controller.StationController.retrieveAllStations()
2018-12-01 23:30:41.660  INFO 21297 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/iheartmedia/station],methods=[POST]}" onto public org.springframework.http.ResponseEntity<com.iheartmedia.dto.StationMixin> com.iheartmedia.controller.StationController.createStation(com.iheartmedia.model.Station,org.springframework.validation.Errors)
2018-12-01 23:30:41.660  INFO 21297 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/iheartmedia/station],methods=[DELETE]}" onto public org.springframework.http.ResponseEntity<com.iheartmedia.dto.StationMixin> com.iheartmedia.controller.StationController.deleteStation(com.iheartmedia.model.Station)
2018-12-01 23:30:41.663  INFO 21297 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2018-12-01 23:30:41.664  INFO 21297 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],produces=[text/html]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
2018-12-01 23:30:41.687  INFO 21297 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2018-12-01 23:30:41.687  INFO 21297 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2018-12-01 23:30:41.979  INFO 21297 --- [           main] com.iheartmedia.model.StationTest        : Started StationTest in 3.704 seconds (JVM running for 4.431)
{station.call.sign.empty}
{station.name.empty}
{station.id.empty}
2018-12-01 22:11:10.360  INFO 18663 --- [       Thread-2] o.s.w.c.s.GenericWebApplicationContext   : Closing org.springframework.web.context.support.GenericWebApplicationContext@6892b3b6: startup date [Sat Dec 01 22:11:06 PST 2018]; root of context hierarchy
2018-12-01 22:11:10.363  INFO 18663 --- [       Thread-2] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2018-12-01 22:11:10.364  INFO 18663 --- [       Thread-2] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown initiated...
2018-12-01 22:11:10.367  INFO 18663 --- [       Thread-2] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown completed.

Process finished with exit code 0

messages.properties 资源包

station.not.found=Station {0} was not found
station.id.empty=Station ID can not be empty.
station.id.format.not.valid=Station ID ${validatedValue} is not valid. Station ID should start with either W or K.
station.name.empty=Station name can not be empty.
station.call.sign.empty=Station call sign can not be empty.

我们的依赖树

我已阅读以下内容

  1. Custom error messaging on Hibernate Validation

  2. Custom Message Key in Hibernate validator not working with message.property

  3. Does Spring Boot automatically resolve message keys in javax and hibernate validation annotations

但我仍然无法弄清楚我做错了什么。

更新

根据@Jonathan Johx 的建议,我将 ResourceBundleMessage 的基本名称更改为messages(也更新了代码 sn-p),但仍然出现错误。

【问题讨论】:

  • 我猜在您的测试类中Station 对象成员为空。 (因为它是使用 this.station = new Station() 创建的)。而且您已经在验证器规则中说明了不能为空的规则。例如:stationIdstationName。如果我错了,请纠正我。
  • 它应该为空。这就是验证失败的原因。但是由于某种原因没有查找消息键。
  • 你能试试messageSource.setBasename("classpath:messages");吗?

标签: spring hibernate spring-boot hibernate-validator


【解决方案1】:

问题是因为 classpath: 确实引用了 ConfigurationHelper 类的文件夹的根目录,但没有找到。尝试将文件名重命名为 Validator 使用的 ValidationMessages.properties 并更新以下行:

messageSource.setBasenames("ValidationMessages"); 

更新

如果您想默认使用验证消息,那么您必须创建一个名为:

ValidationMessages.properties

并添加您认为必要的属性。

【讨论】:

  • 没关系,我看到你的带有 > 符号的 message.properties 文件我不知道为什么 eclipse 会像我这样认为的文件夹。
  • IDE 正在包装文本。
  • 如果您更改为 ValidationMessages.properties 应该可以工作。即使您没有放置 ConfigurationHelper 类
  • 只是“ValidationProperties”的基本名称?
  • ValidationMessages.properties 文件也是如此。重命名它。
猜你喜欢
  • 2018-05-26
  • 2016-03-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-28
  • 2018-09-07
相关资源
最近更新 更多