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

RepositoryRestController处理多HAL URI POST请求的反序列化异常问题

批量创建用户时Spring HATEOAS CollectionModel反序列化异常解决

场景与问题

User与EmploymentType为ManyToOne关联,与Skill为OneToMany关联,通过自定义RepositoryRestController在持久化前做额外校验。单个创建用户并关联EmploymentType的请求正常,但批量创建包含多个Skill HAL URI的用户时,出现JSON反序列化错误,无法将数组转换为CollectionModel。

相关代码

User实体类

@Entity
public class User {
    //..
    private String name;

    @ManyToOne
    private EmploymentType employmentType;

    @OneToMany(mappedBy = "user")
    private Set<Skill> skills = new HashSet<>();
    //..
}

Skill实体类

@Entity
public class Skill {
    //..
    @ManyToOne
    private User user;
    //..
}

EmploymentType实体类

@Entity
public class EmploymentType {
    //..
    @ManyToOne
    private User user;
    //..
}

自定义RepositoryRestController

@RepositoryRestController
public class CustomUserController {
        private UserRepository userRepository;

        @PostMapping(path = "/users/createUserWithEmploymentType")
    public @ResponseBody String  createUserWithEmploymentType(@RequestBody EntityModel<User> users) {
        System.out.println(users);
        // ....
        return "SUCCESS";
    }

        @PostMapping(path = "/users/createUserWithSkillBatch")
    public @ResponseBody String createUserWithSkill(@RequestBody CollectionModel<EntityModel<User>> users) {
        System.out.println(users);
        // ....
        return "SUCCESS";
    }
}

报错信息

...ExceptionHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type org.springframework.hateoas.CollectionModel from Array value (token JsonToken.START_ARRAY); nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize value of type org.springframework.hateoas.CollectionModel from Array value (token JsonToken.START_ARRAY)<EOL> at [Source: (org.springframework.util.StreamUtils$NonClosingInputStream); line: 1, column: 1]]

解决方案

核心原因

Spring HATEOAS的CollectionModel是HAL规范的包裹式结构,它默认期望的JSON格式是包含_embedded和_links的对象,而非直接的数组。如果请求体传的是纯数组,Jackson无法自动完成反序列化。

方法1:调整请求体为HAL标准格式

把请求的JSON从纯数组改成CollectionModel要求的HAL结构,示例:

{
  "_embedded": {
    "users": [
      {
        "name": "张三",
        "skills": [
          {"_links": {"self": {"href": "/skills/1"}}},
          {"_links": {"self": {"href": "/skills/2"}}}
        ],
        "_links": {"self": {"href": "/users/1"}}
      },
      {
        "name": "李四",
        "skills": [
          {"_links": {"self": {"href": "/skills/3"}}}
        ],
        "_links": {"self": {"href": "/users/2"}}
      }
    ]
  },
  "_links": {
    "self": {"href": "/users"}
  }
}

方法2:修改Controller参数类型,手动转换

如果无法调整请求体格式,可将方法参数改为List<EntityModel<User>>,然后在方法内部手动包装成CollectionModel:

@PostMapping(path = "/users/createUserWithSkillBatch")
public @ResponseBody String createUserWithSkill(@RequestBody List<EntityModel<User>> userList) {
    // 手动把列表包装成CollectionModel
    CollectionModel<EntityModel<User>> users = CollectionModel.of(userList);
    System.out.println(users);
    // 执行自定义校验和持久化逻辑
    return "SUCCESS";
}

方法3:配置Jackson支持数组转CollectionModel

通过自定义Jackson模块,让框架能自动将数组反序列化为CollectionModel,创建以下配置类:

@Configuration
public class HateoasConfig {
    @Bean
    public Module collectionModelArrayDeserializerModule() {
        SimpleModule module = new SimpleModule();
        module.addDeserializer(CollectionModel.class, new JsonDeserializer<CollectionModel<?>>() {
            @Override
            public CollectionModel<?> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
                JsonNode arrayNode = p.getCodec().readTree(p);
                ObjectMapper mapper = (ObjectMapper) p.getCodec();
                
                if (arrayNode.isArray()) {
                    // 处理数组格式,转换为CollectionModel
                    List<Object> content = mapper.readValue(arrayNode.traverse(), new TypeReference<List<Object>>() {});
                    return CollectionModel.of(content);
                }
                // 兼容标准HAL格式
                return mapper.readValue(arrayNode.traverse(), CollectionModel.class);
            }
        });
        return module;
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 03:32:55