From c7945ad8fd53f928d40304272e86ff00c6d38dd7 Mon Sep 17 00:00:00 2001 From: ouyangxq <775153912@qq.com> Date: Wed, 30 Sep 2026 11:18:40 +0800 Subject: [PATCH] fix(api): preserve null data in response envelopes --- common-core/pom.xml | 1 + .../baiyue/common/core/api/ApiResponse.java | 4 +- .../web/ApiResponseSerializationTest.java | 43 +++++++++++++++++++ 3 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 common-web/src/test/java/com/baiyue/common/web/ApiResponseSerializationTest.java diff --git a/common-core/pom.xml b/common-core/pom.xml index 956d0ca79..2ef4508d3 100644 --- a/common-core/pom.xml +++ b/common-core/pom.xml @@ -14,6 +14,7 @@ baiyue-common-core + com.fasterxml.jackson.corejackson-annotations org.springframeworkspring-web com.baomidou diff --git a/common-core/src/main/java/com/baiyue/common/core/api/ApiResponse.java b/common-core/src/main/java/com/baiyue/common/core/api/ApiResponse.java index d88fd5b64..488845219 100644 --- a/common-core/src/main/java/com/baiyue/common/core/api/ApiResponse.java +++ b/common-core/src/main/java/com/baiyue/common/core/api/ApiResponse.java @@ -1,5 +1,7 @@ package com.baiyue.common.core.api; +import com.fasterxml.jackson.annotation.JsonInclude; + import java.time.Instant; /** @@ -8,7 +10,7 @@ import java.time.Instant; public record ApiResponse( String code, String message, - T data, + @JsonInclude(JsonInclude.Include.ALWAYS) T data, String requestId, String traceId, Instant timestamp) { diff --git a/common-web/src/test/java/com/baiyue/common/web/ApiResponseSerializationTest.java b/common-web/src/test/java/com/baiyue/common/web/ApiResponseSerializationTest.java new file mode 100644 index 000000000..d1aa71cd1 --- /dev/null +++ b/common-web/src/test/java/com/baiyue/common/web/ApiResponseSerializationTest.java @@ -0,0 +1,43 @@ +package com.baiyue.common.web; + +import com.baiyue.common.core.api.ApiResponse; +import com.baiyue.common.core.api.ResultCode; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Test; +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; + +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; + +class ApiResponseSerializationTest { + @Test + void preservesEnvelopeDataUnderNonNullConfiguration() { + new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(JacksonAutoConfiguration.class)) + .withPropertyValues("spring.jackson.default-property-inclusion=non_null") + .run(context -> { + ObjectMapper mapper = context.getBean(ObjectMapper.class); + JsonNode success = mapper.readTree(mapper.writeValueAsString(ApiResponse.success(null))); + assertThat(success.path("code").asText()).isEqualTo("200"); + assertThat(success.has("data")).isTrue(); + assertThat(success.get("data").isNull()).isTrue(); + assertThat(success.has("requestId")).isFalse(); + + JsonNode failure = mapper.readTree(mapper.writeValueAsString( + ApiResponse.failure(ResultCode.CONFLICT, "conflict", "request-1", "trace-1"))); + assertThat(failure.path("code").asText()).isEqualTo("A0409"); + assertThat(failure.has("data")).isTrue(); + assertThat(failure.get("data").isNull()).isTrue(); + assertThat(failure.path("requestId").asText()).isEqualTo("request-1"); + assertThat(failure.path("traceId").asText()).isEqualTo("trace-1"); + + JsonNode populated = mapper.readTree(mapper.writeValueAsString( + ApiResponse.success(Map.of("status", "SUBMITTED")))); + assertThat(populated.path("data").path("status").asText()).isEqualTo("SUBMITTED"); + }); + } +}