JAX-RS клиент внутри контейнера ISO8601 дата сериализации - PullRequest
0 голосов
/ 04 мая 2018

У меня есть JSON REST API с конечной точкой POST /items, которая ожидает такой объект, как:

{"name": "item_name", "timestamp": "2018-01-01T01:01:01.001"}

Мне нужно вызвать эту конечную точку из приложения JEE7, развернутого на WildFly10.

Для моделирования объекта в Java я определил простой класс DTO:

class ItemDTO {

  private String name;
  private LocalDateTime timestamp;

  // default constructor and getters/setters omitted for brevity
}

По умолчанию сериализация LocalDateTime приводит к сложному объекту JSON вместо строки ISO8601.

Следуя тому, что я нашел в Интернете, я добавил к своим зависимостям:

    <dependency>
        <groupId>com.fasterxml.jackson.datatype</groupId>
        <artifactId>jackson-datatype-jsr310</artifactId>
        <version>${jackson.version}</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.datatype</groupId>
        <artifactId>jackson-datatype-jdk8</artifactId>
        <version>${jackson.version}</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.jaxrs</groupId>
        <artifactId>jackson-jaxrs-json-provider</artifactId>
        <version>${jackson.version}</version>
    </dependency>

С jackson.version = 2.7.4

ClientBuilder builder = ClientBuilder.newBuilder()
  .register(new RequestLogger())
  .register(
    new JacksonJsonProvider(
      new ObjectMapper()
        .registerModule(new JavaTimeModule()) 
        .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
        .configure(SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS, false)
        .setDateFormat(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS"))
      , DEFAULT_ANNOTATIONS)
);

Приведенный выше код настраивает clientBuilder для создания клиентов, оснащенных ObjectMapper, который будет сериализовывать типы даты / времени Java8 в строки ISO8601.

Response creation = builder.build()
  .target("http://localhost:8080/my-api/v1.0/items")
  .request().accept(MediaType.APPLICATION_JSON)
  .post(Entity.entity(itemDTO, MediaType.APPLICATION_JSON));

Неожиданно этот запрос завершается ошибкой , так как LocalDateTime, содержащийся в ItemDTO, все еще сериализуется в сложный объект JSON.

N.B .: Мой REST API вполне способен десериализовать строку ISO8601 в LocalDateTime

N.B .: код, создающий запрос POST, работает отлично, если не выполняется внутри контейнера (т.е. LocalDateTime сериализуется в ISO8601)

1 Ответ

0 голосов
/ 05 мая 2018

"код, создающий запрос POST, работает отлично, если не выполняется внутри контейнера" ​​

Скорее всего, JacksonJsonProvider, который вы регистрируете у клиента, не тот, который используется. В контейнере RESTEasy уже есть модуль Джексона, который автоматически регистрируется клиентом, и это тот, который используется.

Вместо того, чтобы конфигурировать ObjectMapper и передавать его провайдеру, вместо этого вы можете использовать ContextResolver. Поставщик найдет этот преобразователь и получит оттуда ObjectMapper.

@Provider
public class ObjectMapperResolver implements ContextResolver<ObjectMapper> {
    private final ObjectMapper mapper;

    public ObjectMapperResolver() {
        mapper = new ObjectMapper();
        // configure mapper
    }

    @Override
    public ObjectMapper getContext(Class<?> cls) {
        return mapper;
    }
}

Тогда просто зарегистрируйте распознаватель на клиенте.

ClientBuilder builder = ClientBuilder.newBuilder()
        .register(ObjectMapperResolver.class)

Если это является случаем, как я подозреваю, когда уже есть JSON-провайдер, который автоматически регистрируется за кулисами, вам не нужно будет регистрировать другого.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...