Spring Boot中基于Header校验API请求Payload版本匹配问题
你的代码没抛出期望的UnrecognizedPropertyException,核心有两个问题:
1. 错误使用Java对象序列化而非JSON序列化
你当前用ObjectOutputStream把payload转成字节数组,这是Java专属的二进制序列化格式,不是JSON。之后用ObjectMapper解析这个二进制流,本质上是在处理非JSON数据,根本走不到正常的JSON反序列化逻辑,自然不会触发未知属性的异常。
正确做法是用ObjectMapper把payload序列化成JSON字符串,再转成输入流供后续反序列化使用。
2. Jackson默认忽略未知属性
Jackson的ObjectMapper默认配置为FAIL_ON_UNKNOWN_PROPERTIES=false,遇到DTO中不存在的属性时会直接忽略,不会抛出异常。必须显式开启该配置,才能让它在遇到未知属性时抛出UnrecognizedPropertyException。
修正后的代码
public <T extends ...> void validateExpectedPayloadVersion(JsonApi<T> payload, String version) throws IOException { // 用ObjectMapper将payload序列化为JSON字符串,再转为输入流 ObjectMapper jsonMapper = new ObjectMapper(); String payloadJson = jsonMapper.writeValueAsString(payload); InputStream is = new ByteArrayInputStream(payloadJson.getBytes(StandardCharsets.UTF_8)); try (InputStreamReader streamReader = new InputStreamReader(is, StandardCharsets.UTF_8)) { JsonApi<T> payloadRequest; try { payloadRequest = unmarshallToVersion(streamReader, version); } catch (final UnrecognizedPropertyException e) { final String offendingPath = StringUtils.join( e.getPath().stream().map(JsonMappingException.Reference::getFieldName) .collect(Collectors.toList()), "." ); throw new InvalidException(offendingPath); } } } private <T extends ...> JsonApi<T> unmarshallToVersion(final InputStreamReader inputStreamReader, final String version) throws IOException { final ObjectMapper mapper = new ObjectMapper(); mapper.setSerializationInclusion(JsonInclude.Include.ALWAYS); mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); mapper.enable(SerializationFeature.INDENT_OUTPUT); // 关键配置:开启未知属性报错 mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true); switch (version) { case "center.1.0": return (JsonApi<T>) mapper.readValue(inputStreamReader, new TypeReference<JsonApi<CenterDtoV0>>() {}); case "center.1.1": default: return (JsonApi<T>) mapper.readValue(inputStreamReader, new TypeReference<JsonApi<CenterDtoV1>>() {}); } }
额外提示
- 可以把
ObjectMapper做成单例,避免重复创建带来的性能损耗。 - 确保
CenterDtoV0和CenterDtoV1字段定义准确:CenterDtoV0包含city字段,CenterDtoV1包含route字段,非版本专属字段保持一致。
内容的提问来源于stack exchange,提问作者iRaven
相关产品推荐
相关产品推荐

