简体   繁体   English

spring 引导中的 ObjectMapper 可以读取字符串,但在解析标头时不起作用

[英]ObjectMapper in spring boot can read string, but doesn't work when parsing headers

I am trying to give an enum value as a header parameter to my rest endpoint in a spring boot @RestController .我试图在 spring 引导@RestController枚举值作为 header 参数提供给我的 rest 端点。 To that end I put the jackson libraries in my build.gradle file since the autogenerated enum used jackson annotations.为此,我将 jackson 库放在我的build.gradle文件中,因为自动生成的枚举使用了 jackson 注释。 I cannot change the enum code (it is autogenerated from a openapi specification).我无法更改枚举代码(它是从 openapi 规范自动生成的)。 It looks like this:它看起来像这样:

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 + "'");
  }
}

The restcontroller I am using to test looks like this:我用来测试的 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);
    }

}

If I don't supply the Accept header and just let break inside the code I can see that the first line of the code works fine, the application/pdf String is transformed into value so the ObjectMapper did its job using the @JsonCreator method.如果我不提供 Accept header 并让 break 在代码中,我可以看到代码的第一行工作正常, application/pdf字符串被转换为value ,因此ObjectMapper使用@JsonCreator方法完成了它的工作。

However if I pass Accept=application/pdf header along with the request I get an error:但是,如果我通过Accept=application/pdf header 以及请求,我会收到错误消息:

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"

This looks to me as if spring is not using the Jackson provided ObjectMapper , thus ignoring the @JsonCreator method and just trying to resolve the enum by default by looking if there is a key with that provided name.这在我看来好像 spring 没有使用 Jackson 提供的ObjectMapper ,因此忽略了@JsonCreator方法,只是尝试通过查看是否存在具有提供的名称的键来默认解析枚举。

This to me does not make sense, becuase I also only @Autowire the ObjectMapper ,... isn't that the one that spring should also use, how can I force spring to use the correct one for parsing the arguments?这对我来说没有意义,因为我也只有@Autowire ObjectMapper ,...不是 spring 也应该使用的那个,我怎样才能强制 spring 使用正确的解析 ZDBC11CAADABD8BDA9E I tried putting it into a @Configuration and making it a @Bean and @Primary with the same results.我尝试将其放入@Configuration并使其成为具有相同结果的@Bean@Primary

I have a workaround by implementing a converter:我有一个通过实现转换器的解决方法:

@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;
        }
    }
}

But I don't understand why this would be necessary, normally spring automatically puts arguments through the ObjectMapper .但我不明白为什么这是必要的,通常 spring 会自动通过ObjectMapper放置 arguments 。

I think this is working as designed.我认为这是按设计工作的。 Spring only uses the Jackson ObjectMapper for conversion of message bodies (using a registered HttpMessageConverter , specifically the MappingJackson2HttpMessageConverter ). Spring 仅使用 Jackson ObjectMapper来转换消息体(使用已注册的HttpMessageConverter ,特别是MappingJackson2HttpMessageConverter )。

This is documented at https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-ann-typeconversion :这记录在https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-ann-typeconversion

Some annotated controller method arguments that represent String-based request input (such as @RequestParam , @RequestHeader , @PathVariable , @MatrixVariable , and @CookieValue ) can require type conversion if the argument is declared as something other than String.如果参数声明为 String 以外的其他内容,则表示基于字符串的请求输入(例如@PathVariable @MatrixVariable @RequestParam @RequestHeader @CookieValue的一些带注释的 controller 方法 arguments 可能需要类型转换。

For such cases, type conversion is automatically applied based on the configured converters对于这种情况,类型转换会根据配置的转换器自动应用

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

You can use the @RequestBody annotation to have the request body read and deserialized into an Object through an HttpMessageConverter您可以使用@RequestBody注释通过 HttpMessageConverter 读取请求正文并将其反序列化为HttpMessageConverter

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM