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"
}

1 ответ

Решение

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

Если тебе нужно только это 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. Пользователь находится в безымянном модуле загрузчика app)

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

Другие вопросы по тегам