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

Spring Boot 3升级后Spring HATEOAS HAL-FORMS属性验证提示失效的问题咨询

Spring Boot 3升级后Spring HATEOAS HAL-FORMS属性验证提示失效的问题咨询

我最近碰到了一个棘手的问题:升级到Spring Boot 3之后,Spring HATEOAS在HAL-FORMS中不再支持模板关联的属性验证提示了。看起来根源在于Spring Boot切换到了新版本的Hibernate和Jakarta Validation API,但Spring HATEOAS的PropertyUtils组件还没跟上这个变化——它的主分支目前仍在导入javax.validation.constraints.NotNull,和Spring Boot 3依赖的版本存在冲突。

我准备了一个极简的单文件+POM示例,能清晰展示这个问题在Spring Boot 2.7.6下正常工作,升级到3.0.5就失效的情况:

示例POM文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.6</version>
<!-- <version>3.0.5</version> -->
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>spring-hateoas-affordance-sample</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-hateoas-affordance-sample</name>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-hateoas</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>

示例Java代码

package com.example.demo;

import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo;
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.methodOn;

import org.hibernate.validator.constraints.Length;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.hateoas.RepresentationModel;
import org.springframework.hateoas.config.EnableHypermediaSupport;
import org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.constraints.NotNull;
//import jakarta.validation.constraints.NotNull;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.Value;

@SpringBootApplication
@EnableHypermediaSupport(type = HypermediaType.HAL_FORMS)
public class SpringHateoasAffordanceSampleApplication {

public static void main(String[] args) {
SpringApplication.run(SpringHateoasAffordanceSampleApplication.class, args);
}

@RestController
class FooController {
private static final String TEMPLATE = "Hello, %s!";

@GetMapping("/")
public HttpEntity<FooRepresentation> get(
@RequestParam(value = "name", defaultValue = "foo") String name) {
var controller = methodOn(FooController.class);
return new ResponseEntity<>(
new FooRepresentation(String.format(TEMPLATE, name))
.add(linkTo(controller.get(name)).withSelfRel())
.add(linkTo(controller.post(new FooModel("foo"))).withRel("foo"))
, HttpStatus.OK);
}

@PostMapping("/foo")
public HttpEntity<FooModel> post(@RequestBody FooModel fooModel) {
return new ResponseEntity<>(fooModel, HttpStatus.OK);
}
}

@Value
@EqualsAndHashCode(callSuper=false)
class FooRepresentation extends RepresentationModel<FooRepresentation> {
private final String name;
}

@Data
@AllArgsConstructor
public class FooModel {
@NotNull
@Length(min = 2, max = 10)
private String name;
}
}

测试现象对比

  • Spring Boot 2.7.6下的正常响应:访问http://localhost:8080,返回的HAL-FORMS模板里包含了@NotNull和@Length对应的验证提示:

{
"name": "Hello, foo!",
"_links": {
"self": {
"href": "/?name=foo"
},
"foo": {
"href": "/foo"
}
},
"_templates": {
"default": {
"method": "POST",
"properties": [
{
"name": "name",
"required": true,
"minLength": 2,
"maxLength": 10,
"type": "text"
}
],
"target": "/foo"
}
}
}

  • Spring Boot 3.0.5下的异常响应:把POM里的版本改成3.0.5,同时把NotNull的导入换成jakarta.validation.constraints.NotNull,再访问同一个地址,返回的模板里丢失了所有验证相关的提示:

{
"name": "Hello, foo!",
"_links": {
"self": {
"href": "/?name=foo"
},
"foo": {
"href": "/foo"
}
},
"_templates": {
"default": {
"method": "POST",
"properties": [
{
"name": "name",
"type": "text"
}
],
"target": "/foo"
}
}
}

虽然@Length的导入包没有变化,但我猜测它同样是因为版本冲突导致失效的。

想请教大家:有没有什么配置可以临时解决这个问题?或者Spring HATEOAS有没有计划在近期支持Spring Boot 3的这个场景?

备注:内容来源于stack exchange,提问作者g10n

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.23 11:07:44