Spring Data Rest Hateoas Resources object empty when consuming with Feign client in client service
我最近尝试使用 Spring Boot 1.4.1 和 Spring Cloud Camden SR1 复制此视频和此 github 存储库中的 Spring Cloud 示例,但遇到了问题。当客户端服务使用 Feign 客户端调用生产者服务时,反序列化的资源没有内容或链接。
进一步挖掘后,问题似乎与 MappingJackson2HttpMessageConverter 和它的 Jackson ObjectMapper 有关。当我查看伪装客户端的 SpringDecoder 使用的 HttpMessageConverters 时,我看到了 2 个 MappingJackson2HttpMessageConverters 但转换器的 ObjectMappers 都没有注册 Jackson2HalModule。
Spring Data Rest 预约服务应用类:
1 2 3 4 5 6 7 | @EnableDiscoveryClient @SpringBootApplication public class ReservationServiceApplication { public static void main(String[] args) { SpringApplication.run(ReservationServiceApplication.class, args); } } |
预订舱位:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | @Entity public class Reservation extends BaseEntity { private String reservationName; public Reservation(String reservationName) { this.reservationName = reservationName; } protected Reservation() {} public String getReservationName() { return reservationName; } } |
BaseEntity 类具有 ID 和版本。
ReservationRepository 类:
1 2 3 | @RepositoryRestResource public interface ReservationRepository extends CrudRepository<Reservation, Integer> { } |
预订服务 pom.xml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 | <?xml version="1.0" encoding="UTF-8"?> <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> reservation-service</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>reservation-service</name> <description>CRUD Service for Reservations</description> <parent> <groupId>org.springframework.boot</groupId> spring-boot-starter-parent</artifactId> <version>1.4.1.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <jackson.version>2.8.4</jackson.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> spring-cloud-dependencies</artifactId> <version>Camden.SR1</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <!-- Spring Dependencies --> <dependency> <groupId>org.springframework.boot</groupId> spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> spring-cloud-starter-sleuth</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> spring-cloud-starter-zipkin</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> spring-boot-starter-data-rest</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> spring-boot-starter-hateoas</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> spring-boot-starter-data-jpa</artifactId> <exclusions> <exclusion> tomcat-jdbc</artifactId> <groupId>org.apache.tomcat</groupId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.boot</groupId> spring-boot-starter-logging</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> spring-cloud-starter-config</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> spring-cloud-starter-eureka</artifactId> </dependency> <!-- Jackson Dependencies --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> jackson-core</artifactId> <version>${jackson.version}</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> jackson-annotations</artifactId> <version>${jackson.version}</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> jackson-databind</artifactId> <version>${jackson.version}</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.datatype</groupId> jackson-datatype-jsr310</artifactId> <version>${jackson.version}</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.datatype</groupId> jackson-datatype-jdk8</artifactId> <version>${jackson.version}</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.module</groupId> jackson-module-parameter-names</artifactId> <version>${jackson.version}</version> </dependency> <!-- Other Dependencies --> <dependency> <groupId>javax.inject</groupId> javax.inject</artifactId> <version>1</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> commons-lang3</artifactId> <version>3.4</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> commons-collections4</artifactId> <version>4.1</version> </dependency> <dependency> <groupId>org.flywaydb</groupId> flyway-core</artifactId> <version>4.0.3</version> </dependency> <dependency> <groupId>com.h2database</groupId> h2</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>com.zaxxer</groupId> HikariCP</artifactId> <version>2.5.0</version> <scope>runtime</scope> </dependency> <dependency> <groupId>org.hibernate</groupId> hibernate-java8</artifactId> <version>5.0.9.Final</version> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> spring-boot-devtools</artifactId> <optional>true</optional> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project> |
预订客户端应用程序类:
1 2 3 4 5 6 7 8 | @EnableFeignClients @EnableDiscoveryClient @SpringBootApplication public class ReservationClientApplication { public static void main(String[] args) { SpringApplication.run(ReservationClientApplication.class, args); } } |
Reservation Client Feign Client 类:
1 2 3 4 5 | @FeignClient("reservation-service") public interface ReservationServiceClient { @RequestMapping(method = RequestMethod.GET, value ="reservations") Resources<Reservation> getReservations(); } |
Reservation Client ReservationApiGateway 控制器类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | @RestController @RequestMapping("reservations") public class ReservationApiGateway { private final ReservationServiceClient client; public ReservationApiGateway(ReservationServiceClient client) { this.client = client; } @GetMapping public Collection<Reservation> reservations() { return client.getReservations().getContent(); } } |
预订客户端 pom.xml
http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 | <groupId>com.example</groupId> reservation-client</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>reservation-client</name> <description>Example reservation client</description> <parent> <groupId>org.springframework.boot</groupId> spring-boot-starter-parent</artifactId> <version>1.4.1.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <jackson.version>2.8.4</jackson.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> spring-cloud-dependencies</artifactId> <version>Camden.SR1</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> spring-cloud-starter-config</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> spring-cloud-starter-sleuth</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> spring-cloud-starter-zipkin</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> spring-boot-devtools</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> spring-boot-starter-hateoas</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> spring-cloud-starter-feign</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> spring-cloud-starter-zuul</artifactId> </dependency> <!-- Jackson Dependencies --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> jackson-core</artifactId> <version>${jackson.version}</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> jackson-annotations</artifactId> <version>${jackson.version}</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> jackson-databind</artifactId> <version>${jackson.version}</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.datatype</groupId> jackson-datatype-jsr310</artifactId> <version>${jackson.version}</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.datatype</groupId> jackson-datatype-jdk8</artifactId> <version>${jackson.version}</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.module</groupId> jackson-module-parameter-names</artifactId> <version>${jackson.version}</version> </dependency> <!-- Other Dependencies --> <dependency> <groupId>org.apache.commons</groupId> commons-lang3</artifactId> <version>3.4</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> commons-collections4</artifactId> <version>4.1</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project> |
当我点击客户端上的 /reservations 端点时,我得到一个空的 JSON 数组 ([])。当我在 feign 客户端上启用 FULL 登录时,我会从预订服务中看到正确的预订。
最后,我尝试添加:
1 | @EnableHypermediaSupport(type=EnableHypermediaSupport.HypermediaType.HAL) |
到 Reservation Client 应用程序类。这导致保留资源被客户端反序列化并正确返回。不幸的是,我尝试了一些其他类,它们具有 Java 8 LocalDateTime 字段,这些字段需要在 ObjectMapper 上注册 Java 8 Jackson 模块,但是当我查看正在使用的 ObjectMapper 时,唯一注册的模块是 Jackson2HalModule。
如果我这样做不正确或是否存在错误,我将非常感谢任何建议。谢谢!
经过更多的挖掘,我能够解决这个问题。客户端应用程序需要 spring-data-rest 依赖项来正确设置消息转换器。不幸的是,考虑到客户端应用程序不需要 Spring 数据存储库,这似乎有点违反直觉。