Сериализация тела WebFlux для модели, зависящей от времени выполнения? - PullRequest
1 голос
/ 19 марта 2020

Когда есть ClientResponse от WebClient, в простейшем случае мы используем clientResponse.bodyToMono(MyResponseModel.class) для сериализации тела ответа.

Но не ясно, каков путь к go, когда ответы ' Формат может отличаться в зависимости от ситуации.

Например, когда ответом может быть один из двух типов

type 1: {"a": <number>, "b": <string>}
type 2: {"c": <date>, "d": {"items": <array>}}

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

  1. попытаться сериализовать к типу 1
  2. , если OK, вернуть модель типа 1
  3. , иначе попытаться сериализовать к типу 2
  4. , если OK вернуть модель типа 2
  5. , иначе ошибка сериализации

Как правильно обрабатывать этот сценарий сериализации с помощью Spring WebFlux?

Ответы [ 2 ]

1 голос
/ 20 марта 2020

Я могу понять, что вы пытаетесь сделать, но рекомендую вам не go идти по этому пути. Архитектура RESTful имеет свои плюсы и минусы, как и все остальное, и одним из ее преимуществ является наблюдаемость , способность четко различать guish то, что течет в провод. Другое преимущество, которое вы оставите, это кешируемость .

Использование ресурсов HTTP имеет первостепенное значение в RESTful API. Это продиктовано одним из архитектурных ограничений REST, а именно Унифицированный интерфейс . Это означает, что ваши API должны быть основаны на ресурсах, и каждый ресурс в системе должен иметь только один логический URI.

В вашем случае у вас есть два разных представления ресурсов, поэтому вы должны обслуживать их через разные URI.

В модели зрелости Ричардсона 1018 * это известно как Уровень 1 .

1 голос
/ 20 марта 2020

Вы можете десериализовать ответ JSON самостоятельно. Сначала получите ответ как json String, а затем выполните десериализацию самостоятельно с помощью предоставленного и настроенного Jackson ObjectMapper, и, если есть JsonMappingException, попробуйте его с моделью 2. Это может быть не так эффективно, как это делает пружина внутренне с Jackson2JsonDecoder, поскольку он обрабатывает InputStream напрямую, не создавая копию, но это, кажется, единственный способ сделать это с подходом try / catch в настоящее время.

@RestController
class Controller {

    private ObjectMapper objectMapper;

    public Controller(ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
    }

    @GetMapping("/handler")
    public Mono<Object> getHandler() {
        return WebClient
            .create("https://baseurl.com")
            .get()
            .uri("/someUri")
            .retrieve()
            .bodyToMono(String.class)
            .flatMap(json ->
                Mono.<Object>fromCallable(() -> objectMapper.readValue(json, Model1.class))
                .onErrorResume(JsonMappingException.class, e ->
                        Mono.fromCallable(() -> objectMapper.readValue(json, Model2.class))
                )
            );
    }
}

Другим решением будет создание базового класса или интерфейса для типов ответов, а затем реализация пользовательского Джексона JsonDeserializer для него. Это может определить, какой из типов следует использовать, а затем выполнить десериализацию.

...