Обмен SpringToot RestTemplate, имеющий проблему с преобразованием ParameterizedTypeReference для ResponseEntity - PullRequest
0 голосов
/ 19 февраля 2019

Я строю два микросервиса, которые должны общаться друг с другом.Я использую Eureka в качестве реестра служб.

Микросервис 1 - Microservice1.java

@SpringBootApplication
public class Microservice1Application {
    public static void main(String[] args) {
        SpringApplication.run(Microservice1Application.class, args);
    }
}

Microservice1Controller.java

@RestController
@RequestMapping("/getdata")
public class Microservice1Controller {  
    @GetMapping(value = "/") 
public ResponseEntity<Microservice1ResponseWrapper<List<Customer1>>> getAll() {
        List<Customer1> list = //get data from repository

        return new ResponseEntity<Microservice1ResponseWrapper<List<Customer1>>>(new Microservice1ResponseWrapper<List<Customer1>>(Microservice1ResponseStatus.SUCCESS,list);
    }
}

Microservice1ResponseWrapper.java - это универсальная оболочка

public class Microservice1ResponseWrapper<T> {
private Microservice1ResponseStatus status;
private T data;
//constructor, getter and setters
}

applicationProperties.yaml

spring:
  application:
    name: microservice1

server:
  port: 8073

Microservice2 Microservice2, который будет получать данные из Microservice1

@SpringBootApplication
public class Microservice2Application {
    public static void main(String[] args) {
        SpringApplication.run(Microservice2Application.class, args);
    }
}

@Configuration
class Config {

    @LoadBalanced
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

Microservice2Controller.java

@RestController
@RequestMapping("/fetchdata")
public class Microservice2Controller {  
@Autowired
    private RestTemplate restTemplate;


    @GetMapping(value = "/")
public ResponseEntity<Microservice2ResponseWrapper<List<Customer2>>> getAll() {     
            String getAllUrl = "http://microservice1/getdata/";
            ParameterizedTypeReference<Microservice2ResponseWrapper<List<Customer2>>>  parameterizedTypeReference = 
                    new ParameterizedTypeReference<Microservice2ResponseWrapper<List<Customer2>>>(){};

            ResponseEntity<Microservice2ResponseWrapper<List<Customer2>>> listData =
                    restTemplate.exchange(getAllUrl, HttpMethod.GET, null,parameterizedTypeReference);          
            return listData;
    }   
}

Microservice2ResponseWrapper.java - это универсальная оболочка

public class Microservice2ResponseWrapper<T> {
private Microservice2ResponseStatus status;
private T data;
//constructor, getter and setters
}

applicationProperties.yaml

spring:
  application:
    name: microservice2

server:
  port: 8074

Customer1 (в Microservice1) и Customer2 (Microservice2) - практически идентичные объекты.

public class Customer1 implements Serializable {

    private static final long serialVersionUID = 1L;

    private Long custId;

    private String custName;

    private String firstName;

    private String lastName;

    private Long age;

    public Customer1() {
    }

    public Customer1(String custName, String firstName, String lastName, Long age) {
        this.custName = custName;
        this.firstName = firstName;
        this.lastName = lastName;
        this.age = age;
    }

    public Customer1(Long custId, String custName, String firstName, String lastName, Long age) {
        this.custId = custId;
        this.custName = custName;
        this.firstName = firstName;
        this.lastName = lastName;
        this.age = age;
    }

//getter, setter and toString
}

Customer2.java в Microservice2

public class Customer2 implements Serializable {

    private static final long serialVersionUID = 1L;

    private Long custId;

    private String custName;

    private String firstName;

    private String lastName;

    private Long age;

    public Customer2() {
    }

//getter, setter and toString
}

Когда я запускаю Microservice1: http://localhost:8073/getdata, он получает данные из базы данных и работает нормально.Вот ответ, который я вижу на экране:

<Microservice1ResponseWrapper>
<status>SUCCESS</status>
<data>
<custId>1</custId>
<custName>string1</custName>
<firstName>string1</firstName>
<lastName>string1</lastName>
<age>30</age>
</data>
</Microservice1ResponseWrapper>

Когда я запускаю Microservice2: http://localhost:8074/fetchdata, он должен перейти к Microservice 1 и получить данные.

Однако я получаюошибка типа:

org.springframework.web.client.RestClientException: Error while extracting response for type
at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:117)
    at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:994)
    at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:977)

Caused by: org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot construct instance of `com.rest.Customer2` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('1'); nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `com.rest.Customer2` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('1')
 at [Source: (PushbackInputStream); line: 1, column: 61] (through reference chain: com.rest.wrapper.Microservice2ResponseWrapper["data"]->java.util.ArrayList[0])
    at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:245)
    at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.read(AbstractJackson2HttpMessageConverter.java:227)
    at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:102)
    ... 77 more

Caused by: com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `com.rest.Customer2` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('1')
 at [Source: (PushbackInputStream); line: 1, column: 61] (through reference chain: com.rest.wrapper.Microservice2ResponseWrapper["data"]->java.util.ArrayList[0])
    at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:63)
    at com.fasterxml.jackson.databind.DeserializationContext.reportInputMismatch(DeserializationContext.java:1343)
    at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1032)
    at com.fasterxml.jackson.databind.deser.ValueInstantiator._createFromStringFallbacks(ValueInstantiator.java:371)
    at com.fasterxml.jackson.databind.deser.std.StdValueInstantiator.createFromString(StdValueInstantiator.java:323)
    at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromString(BeanDeserializerBase.java:1373)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeOther(BeanDeserializer.java:171)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:161)
    at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:286)
    at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:245)
    at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:27)
    at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:127)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:369)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:159)
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4013)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3084)
    at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:239) 

Я делаю какую-либо ошибку в ParameterizedTypeReference или перезапускаем обменный вызов?

ПРИМЕЧАНИЕ. Если я запускаю эти два микросервисабез регистрации Eureka они работают абсолютно нормально.Но в тот момент, когда я знакомлюсь с Eureka и регистрирую эти два сервиса в Eureka, я получаю проблему, как указано выше.Для этого я только что изменил контроллер Miroservice2: String getAllUrl = "http://localhost:8073/getdata/";


Обновление - 02/22/19

вот что я пробовал

обновлен Microservice1Controller-getAll (), как показано ниже:

    @GetMapping(value = "/") 
public ResponseEntity<List<Customer1>> getAll() {
        List<Customer1> list = //get data from repository

        return new ResponseEntity<List<Customer1>>(list);
    }

Обновлен метод Microservice2Controller-getAll ()

    @GetMapping(value = "/")
public ResponseEntity<List<Customer2>> getAll() {     
            String getAllUrl = "http://microservice1/getdata/";
            ParameterizedTypeReference<List<Customer2>>  parameterizedTypeReference = 
                    new ParameterizedTypeReference<List<Customer2>>(){};

            ResponseEntity<List<Customer2>> listData =
                    restTemplate.exchange(getAllUrl, HttpMethod.GET, null,parameterizedTypeReference);          
            return listData;
    }   

Это сработало - вызов Microservice1 из Microservice2, как указано в предыдущем описании. Возврат Microservice1ResponseEntity> в Microservice2 и Microservice2, преобразовывающие это в ResponseEntity>.

Однако Microservice1, возвращающий ResponseEntity<Microservice1ResponseWrapper<List<Customer1>>> в Microservice2 и Microservice2, не может быть в состоянии преобразовать в ResponseEntity<Microservice2ResponseWrapper<List<Customer2>>>.


ОБНОВЛЕНИЕ 06/28/19

Если я сделаю следующее изменение в контроллере Microservice2, то у меня возникнут 2 проблемы:

  1. начнется ошибка LinkedHashMap. Java.lang.ClassCastException: java.util.LinkedHashMap не может быть приведен к java.util.List
  2. Он не извлекает все записи, он просто извлекает последний элемент из списка, например, есть 2 пользователя, затем ят показывает только одного последнего пользователя и не всех.
ParameterizedTypeReference<Microservice2ResponseWrapper>  parameterizedTypeReference = 
                      new ParameterizedTypeReference<Microservice2ResponseWrapper>(){};

ResponseEntity<Microservice2ResponseWrapper> listData =
                      restTemplate.exchange(getAllUrl, HttpMethod.GET, null,parameterizedTypeReference);
List ls = (List) listData.getBody().getData();

//if I print listData.getBody().getData() then it just shows only one record of users.

Ответы [ 2 ]

0 голосов
/ 03 июля 2019

Я не смог решить ее с помощью RestTemplate.Поэтому я выбрал вариант использования Open Feign.

Шаг 1. В pom.xml я добавил зависимость

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-openfeign-core</artifactId>
    </dependency>

Шаг 2. Добавьте клиентский интерфейс

@FeignClient(name = "microservice1")
public interface Microservice1Client {
 @GetMapping(value = "/getdata/") 
 ResponseEntity<Microservice1ResponseWrapper<List<Customer1>>> getAll();
}

Шаг 3. В Microservice2Controller внедрить бин FeignClient Microservice1Client

@RestController
@RequestMapping("/fetchdata")
public class Microservice2Controller {  
@Autowired
Microservice1Client microservice1Client;

    @GetMapping(value = "/")
public ResponseEntity<Microservice2ResponseWrapper<List<Customer2>>> getAll() {     
            return microservice1Client.getAll();          
    }   
}
0 голосов
/ 20 февраля 2019

Вам необходимо создать конструктор по умолчанию для класса Customer2.Customer2 () {}

...