Как обработать HAL-ответ сервера для клиента с помощью Spring HATEOAS + Fiegn? - PullRequest
0 голосов
/ 07 мая 2020

Как правильно настроить и обработать ответ REST в формате HAL с помощью Spring HATEOAS и Feign?
Я пытаюсь обработать такой ответ с сервера:

{
  "items" : [ {
    "StandardReceiptId" : 300000005930306,
    "ReceiptNumber" : "00000000289",
    … a lot of attributes here
   "@context" : {
      "key" : "300000005930306",
      "headers" : {
        "ETag" : "ACED0005…
      },
      "links" : [ {
        "rel" : "self",
       …
    } ]
    }
  } ],
  "count" : 1,
  "hasMore" : true,
  "limit" : 1,
  "offset" : 0,
  "links" : [ {
    "rel" : "self",
    "href" : "https://server.oraclecloud.com:444/fscmRestApi/resources/11.13.18.05/standardReceipts",
    "name" : "standardReceipts",
    "kind" : "collection"
  } ]
}

Когда я пытался десериализовать это ответ с таким типом возврата CollectionModel<EntityModel<StandardReceipts>> all( для симуляции, я получил такую ​​ошибку:

com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.util.ArrayList<org.springframework.hateoas.EntityModel<ru.otus.spring.ocae.model.StandardReceipt>>` out of START_OBJECT token

Когда я попробовал EntityModel<Iterable<StandardReceipt>> all(, то получил следующее:

java.lang.ClassCastException: class java.util.LinkedHashMap cannot be cast to class java.lang.Iterable (java.util.LinkedHashMap and java.lang.Iterable are in module java.base of loader 'bootstrap')

Я пробовал разные объявления для возвращаемого типа, но мне удалось получить мои предметы, только объявить его как EntityModel<Map<String,StandardReceipt>> Я не думаю, что это правильный путь.

Код:

@Configuration
public class FeignConfig {

    @Bean
    public Decoder feignDecoder() {

        ObjectMapper mapper = new ObjectMapper()
                .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
                .registerModule(new Jackson2HalModule());

        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        converter.setSupportedMediaTypes(MediaType.parseMediaTypes("application/hal+json"));
        converter.setObjectMapper(mapper);

        ObjectFactory<HttpMessageConverters> objectFactory = () -> new HttpMessageConverters(converter);

        return new ResponseEntityDecoder(new SpringDecoder(objectFactory));
    }
@FeignClient(value = "standardReceipts", url ="https://server.oraclecloud.com")
public interface StandardReceiptClient {

    @GetMapping("/fscmRestApi/resources/11.13.18.05/standardReceipts")
     EntityModel<Map<String,StandardReceipt>> all(
            @RequestHeader("Authorization") String authHeader,
            @RequestHeader(value = "REST-Framework-Version", defaultValue = "7") String restFrameworkVersion,
            @RequestParam(required = false, defaultValue = "0", value = "offset") int offset,
            @RequestParam(required = true, defaultValue = "1", value = "limit") int limit
    );
}
@RestController
public class StandardReceiptsController {

    StandardReceiptClient standardReceiptClient;
    private String token;

    public StandardReceiptsController(
            @Autowired StandardReceiptClient standardReceiptClient,
            @Value("${oracle.jwt.token}") String token
    ) {
        this.token = token;
        this.standardReceiptClient = standardReceiptClient;
    }

    @GetMapping(value = "/standardreceipts")
    public @ResponseBody List<StandardReceipt> all (
            @RequestParam(required = false, defaultValue = "0", value = "offset") int offset,
            @RequestParam(required = true, defaultValue = "1", value = "limit") int limit
    ) {
         EntityModel<Map<String,StandardReceipt>> coll =
                 standardReceiptClient.all("Bearer " + token,"7", offset, limit);
        Object list = coll.getContent().get("items");
        return (ArrayList)list;
    }
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class StandardReceipt {
    long   StandardReceiptId; //Standard Receipt ID
    String ReceiptNumber; //Receipt Number
… a lot of attributes
@EnableHypermediaSupport(type = EnableHypermediaSupport.HypermediaType.HAL)
@EnableFeignClients
@SpringBootApplication 
public class OracleCloudAppExtensionApplication {
   public static void main(String[] args) {
      SpringApplication.run(OracleCloudAppExtensionApplication.class, args);
   }
}

Пом. xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <parent>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-parent</artifactId>
      <version>2.2.6.RELEASE</version>
      <relativePath/>
   </parent>
   <groupId>ru.otus.spring</groupId>
   <artifactId>oracle-cloud-app-extension</artifactId>
   <version>0.0.1-SNAPSHOT</version>
   <name>oracle-cloud-app-extension</name>
   <description>Oracle Cloud Application extension (OCAE)</description>

   <properties>
      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
      <java.version>11</java.version>
   </properties>

   <dependencies>
      <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter</artifactId>
      </dependency>
      <dependency>
         <groupId>org.springframework.cloud</groupId>
         <artifactId>spring-cloud-starter-openfeign</artifactId>
         <version>2.2.2.RELEASE</version>
      </dependency>
      <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-hateoas</artifactId>
      </dependency>
      <dependency>
         <groupId>org.projectlombok</groupId>
         <artifactId>lombok</artifactId>
         <optional>true</optional>
      </dependency>

      <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-test</artifactId>
         <scope>test</scope>
         <exclusions>
            <exclusion>
               <groupId>org.junit.vintage</groupId>
               <artifactId>junit-vintage-engine</artifactId>
            </exclusion>
         </exclusions>
      </dependency>
      <dependency>
         <groupId>org.springframework.batch</groupId>
         <artifactId>spring-batch-test</artifactId>
         <scope>test</scope>
      </dependency>
      <dependency>
         <groupId>org.springframework.hateoas</groupId>
         <artifactId>spring-hateoas</artifactId>
         <version>1.0.5.RELEASE</version>
      </dependency>
   </dependencies>

   <build>
      <plugins>
         <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
         </plugin>
      </plugins>
   </build>

</project>
...