返回

如何解决 Spring Boot 中自定义 ResponseEntity 反序列化失败的问题?

java

如何解决自定义 ResponseEntity 反序列化失败的问题

介绍

在使用 Spring Boot 开发 Web 应用程序时,我们经常使用 ResponseEntity 来封装 HTTP 响应。有时候,我们需要定义自定义的 ResponseEntity 子类来满足特定的需求。但是,在反序列化自定义 ResponseEntity 时,可能会遇到问题,尤其是当类具有带参数的构造函数时。

问题:反序列化失败

Spring Boot 使用 Jackson 库进行 JSON 序列化和反序列化。默认情况下,Jackson 会查找具有无参构造函数的类来进行反序列化。但是,如果自定义 ResponseEntity 子类具有带参数的构造函数,Jackson 就无法找到合适的构造函数,从而导致反序列化失败。

解决方案:使用 @JsonCreator 注解

为了解决这个问题,需要在自定义 ResponseEntity 子类中添加 @JsonCreator 注解。此注解告诉 Jackson 使用带参数的构造函数进行反序列化。它指定了应该如何从 JSON 输入中解析参数。

示例:使用 @JsonCreator

考虑以下 CircuitBreakerFallbackMessage 类,它继承了 ResponseEntity 并具有带参数的构造函数:

import lombok.Getter;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;

import java.util.HashMap;
import java.util.Map;

@Getter
public static class CircuitBreakerFallbackMessage extends ResponseEntity<Map<String, String>> {
    @JsonCreator
    public CircuitBreakerFallbackMessage(String message) {
        super(buildMessage(message), HttpStatus.GATEWAY_TIMEOUT);
    }

    private static Map<String, String> buildMessage(String appName) {
        String message = MessageFormat.format("{0} is currently unavailable", appName);
        HashMap<String, String> messageMap = new HashMap<>();
        messageMap.put("message", message);
        return messageMap;
    }
}

@JsonCreator 注解中,我们指定了使用具有 String 参数的构造函数进行反序列化。

更新测试

在使用 @JsonCreator 注解修复了反序列化问题之后,测试应该能够成功运行:

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest;
import org.springframework.http.HttpStatus;
import org.springframework.test.web.reactive.server.WebTestClient;

import java.text.MessageFormat;
import java.util.Map;
import java.util.regex.Pattern;

import static org.assertj.core.api.Assertions.assertThat;

@WebFluxTest(controllers = FallbackController.class)
class FallbackControllerTest {
    @Autowired
    WebTestClient testClient;

    @Test
    void testGetFallback() {
        String appName = "some-app";
        CircuitBreakerFallbackMessage fallbackMessage = testClient
                .get()
                .uri(MessageFormat.format("/fallback/{0}", appName))
                .exchange()
                .expectStatus().isEqualTo(HttpStatus.GATEWAY_TIMEOUT)
                .expectBody(CircuitBreakerFallbackMessage.class)
                .returnResult()
                .getResponseBody();
        assertThat(fallbackMessage).isNotNull();
        Map<String, String> fallbackMessageBody = fallbackMessage.getBody();
        assertThat(fallbackMessageBody).isNotNull();
        assertThat(fallbackMessage)
                .extracting(fallbackMessageBody.get("message"))
                .asString()
                .containsPattern(Pattern.compile(appName + " (is )?(currently )?unavailable"));
    }
}

结论

通过添加 @JsonCreator 注解来显式指定反序列化的构造函数,我们解决了自定义 ResponseEntity 反序列化失败的问题。这确保了 Jackson 可以正确地将 JSON 输入反序列化为我们的自定义 ResponseEntity 子类。

常见问题解答

Q:为什么使用 @JsonCreator 注解?
A:@JsonCreator 注解告诉 Jackson 使用带参数的构造函数进行反序列化。

Q:如果自定义 ResponseEntity 子类没有带参数的构造函数,我该怎么办?
A:在这种情况下,需要手动添加一个无参构造函数,以便 Jackson 能够使用它进行反序列化。

Q:@JsonCreator 注解可以应用于其他类吗?
A:是的,@JsonCreator 注解可以应用于任何需要使用带参数的构造函数进行反序列化的类。

Q:如何使用其他 Jackson 注解来控制反序列化?
A:Jackson 提供了其他注解,例如 @JsonProperty@JsonDeserialize,它们可以更精细地控制反序列化过程。

Q:在哪里可以找到有关 Jackson 库的更多信息?
A:Jackson 库的官方网站提供了全面的文档和示例:https://fasterxml.github.io/jackson-databind/