HTTP-клиент Micronaut — десериализация универсального типа — для тестирования API

Для тестирования API мне нужно разобрать ответ на запрос, используя io.micronaut.http.client.HttpClient.

Я предпочитаю использовать формат, указанный ниже.

Response<User> response =
                client.toBlocking().retrieve(HttpRequest.POST("/", user), Response.class);

Но это не работает. Я получаю ошибку приведения типа при попытке получить пользователя из ответа

User user = response.getUser();

В настоящее время я использую ObjectMapper (Jackson), как показано ниже,

String response = client.toBlocking().retrieve(HttpRequest.POST("/", user), String.class);
ObjectMapper objectMapper = new ObjectMapper();
Response<User> response = objectMapper.readValue(response, new TypeReference<Response<User>>() {});

Есть ли способ использовать TypeReference напрямую с методом извлечения HttpClient? Или любым другим способом использовать общий тип класса с HttpClient.

Другая полезная информация

// Response generic class
class Response<T> {
T data;
....
}

Пример ответа

{
    "data": {
        "name":"sample"
    },
    "message": "Success"
}

person Sujith C P    schedule 28.04.2020    source источник


Ответы (2)


У вас есть несколько вариантов:

Если вам нужен только этот экземпляр объекта User, вы можете сделать это с помощью метода retrieve(), где вторым параметром является класс домена (User в вашем случае), а затем вы можете использовать переменную user по мере необходимости:

User user = client.toBlocking().retrieve(HttpRequest.POST("/", user), User.class);

assertThat(user)
    .isNotNull()
    .extracting(User::getName)
    .isEqualTo("sample");

Если вам также нужен объект ответа (например, чтобы иметь возможность проверить код ответа), вам следует использовать метод exchange(), а затем получить экземпляр User, вызвав метод body() для этого ответа:

HttpResponse<User> response = client.toBlocking()
    .exchange(HttpRequest.POST("/", user), User.class);

assertThat(response)
    .isNotNull()
    .extracting(HttpResponse::body)
    .extracting(User::getName)
    .isEqualTo("sample");

Обновление: когда вам нужно использовать универсальный класс в качестве ответа, вы должны использовать Argument.of() в качестве второго параметра методов retrieve() и exchange(), где вы можете указать универсальный класс ответа и параметры его типа. См. пример:

Response<User> response = client.toBlocking()
    .retrieve(HttpRequest.POST("/", user), Argument.of(Response.class, User.class));

assertThat(response)
    .isNotNull()
    .extracting(Response::getData)
    .extracting(User::getName)
    .isEqualTo("sample");

Если вы сделаете это без Argument.of(), то свойство data будет преобразовано в LinkedHashMap вместо User, и это:

Response<User> response = client.toBlocking().retrieve(HttpRequest.POST("/", user), Response.class);
response.getData().getName();

... выкинет ClassCastException:

java.lang.ClassCastException: класс java.util.LinkedHashMap не может быть приведен к классу my.app.domain.User (java.util.LinkedHashMap находится в модуле java.base загрузчика 'bootstrap'; my.app.domain.User. Пользователь находится в безымянном модуле загрузчика «приложение»)

person cgrim    schedule 28.04.2020
comment
Привет, спасибо за ответ. Response — это настраиваемый универсальный класс для переноса ответа API. Таким образом, правильным типом рассматриваемого десериализованного ответа API будет Response‹User›. Например. Класс ответа Ответ ‹T›{ T данные; ‹другие поля› } Я хочу извлечь поле данных из типа Response‹User›. - person Sujith C P; 28.04.2020
comment
Хорошо, извините, я неправильно понял ваш вопрос. Поэтому я добавил также пример с универсальным классом в качестве ответа. - person cgrim; 28.04.2020

Просто как очень простая альтернатива: когда ваши ответы в формате JSON, вы всегда можете просто десериализовать их в Map.class. Быстро и легко. Конечно, тогда у вас нет безопасности типов.

person Robert    schedule 25.11.2020