ObjectMapper при весенней загрузке может читать строку, но не работает при разборе заголовков - PullRequest
1 голос
/ 24 октября 2019

Я пытаюсь передать значение enum в качестве параметра заголовка моей конечной точке покоя при весенней загрузке @RestController. С этой целью я поместил библиотеки Джексона в мой файл build.gradle, так как автоматически сгенерированное перечисление использовало аннотации Джексона. Я не могу изменить код enum (он автоматически генерируется из спецификации openapi). Это выглядит так:

public enum DocumentTypes {

  APPLICATION_PDF("application/pdf"),

  APPLICATION_RTF("application/rtf"),

  APPLICATION_VND_OASIS_OPENDOCUMENT_TEXT("application/vnd.oasis.opendocument.text"),

  APPLICATION_VND_OPENXMLFORMATS_OFFICEDOCUMENT_WORDPROCESSINGML_DOCUMENT("application/vnd.openxmlformats-officedocument.wordprocessingml.document"),

  APPLICATION_VND_MS_WORD("application/vnd.ms-word"),

  TEXT_HTML("text/html"),

  TEXT_PLAIN("text/plain");

  private String value;

  DocumentTypes(String value) {
    this.value = value;
  }

  @Override
  @JsonValue
  public String toString() {
    return String.valueOf(value);
  }

  @JsonCreator
  public static DocumentTypes fromValue(String text) {
    for (DocumentTypes b : DocumentTypes.values()) {
      if (String.valueOf(b.value).equals(text)) {
        return b;
      }
    }
    throw new IllegalArgumentException("Unexpected value '" + text + "'");
  }
}

Restcontroller, который я использую для тестирования, выглядит следующим образом:

@RestController
@RequestMapping("/test")
public class TestController {

    @Autowired
    private ObjectMapper objectMapper;

    @RequestMapping(path = "", method = RequestMethod.GET)
    public void test(@RequestHeader(value = "Accept", required = false) DocumentTypes targetFormat) throws IOException {
        DocumentTypes value = objectMapper.readValue("\"application/pdf\"", DocumentTypes.class);
    }

}

Если я не предоставлю заголовок Accept и просто позволю разрываться внутри кодаЯ вижу, что первая строка кода работает нормально, строка application/pdf преобразуется в value, поэтому ObjectMapper выполнила свою работу, используя метод @JsonCreator.

Однако, если я передам *Заголовок 1015 * вместе с запросом я получаю сообщение об ошибке:

Failed to convert value of type 'java.lang.String' to required type 'de.some.namespace.model.DocumentTypes'; 
nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [@org.springframework.web.bind.annotation.RequestHeader de.some.namespace.model.DocumentTypes] for value 'application/pdf'; 
nested exception is java.lang.IllegalArgumentException: No enum constant de.some.namespace.model.DocumentTypes.application/pdf"

Мне кажется, что Spring не использует предоставленный Джексоном ObjectMapper, игнорируя, таким образом, метод @JsonCreator и просто пытаясь решитьперечисление по умолчанию, если есть ключ с указанным именем.

Это для меня не имеет смысла, потому что я также только @Autowire ObjectMapper, ... разве не тот, который должна использовать пружина, как я могу заставить пружину использовать правильнуюодин для разбора аргументов? Я попытался поместить его в @Configuration и сделать его @Bean и @Primary с теми же результатами.

У меня есть обходной путь, реализовав конвертер:

@Component
public class StringToDocumentTypesConverter implements Converter<String, DocumentTypes> {

    @Autowired
    private ObjectMapper mapper;

    @Override
    public DocumentTypes convert(String s) {
        try {
            return mapper.readValue(String.format("\"%s\"", s), DocumentTypes.class);
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }
}

НоЯ не понимаю, почему это было бы необходимо, обычно Spring автоматически выставляет аргументы через ObjectMapper.

1 Ответ

1 голос
/ 28 октября 2019

Я думаю, что это работает как задумано. Spring использует только Jackson ObjectMapper для преобразования тел сообщений (используя зарегистрированный HttpMessageConverter, в частности MappingJackson2HttpMessageConverter).

Это задокументировано в https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-ann-typeconversion:

Некоторые аннотированные аргументы метода контроллера, которые представляют ввод запроса на основе строки (например, @RequestParam, @RequestHeader, @PathVariable, @MatrixVariable и @CookieValue), могут потребовать преобразования типа, если аргумент объявлен как-то отличный от String.

Для таких случаев автоматически применяется преобразование типов на основе настроенных преобразователей

И https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-ann-requestbody:

Можно использовать @RequestBodyаннотация для чтения тела запроса и десериализации в объект через HttpMessageConverter

...