jackson 如何让@RestController POST方法忽略Content-Type头而只使用请求主体?

yzckvree  于 2022-11-08  发布在  其他
关注(0)|答案(3)|浏览(165)

我使用的是最新的Sping Boot (1.2.1)和它附带的任何Spring MVC版本。
我有一个控制器方法,它对传入和传出数据都进行隐式JSON转换:

@RestController
public class LoginController {

    @RequestMapping(value = "/login", method = POST, produces = "application/json")
    ResponseEntity<LoginResponse> login(@RequestBody LoginRequest loginRequest) {
        // ...
    }
}

这是正常的,但只有当请求Content-Type被设置为application/json时,它才能正常工作。在所有其他情况下,无论请求主体是什么,它都会以415响应:

{
"timestamp": 1423844498998,
"status": 415,
"error": "Unsupported Media Type",
"exception": "org.springframework.web.HttpMediaTypeNotSupportedException",
"message": "Content type 'text/plain;charset=UTF-8' not supported",
"path": "/login/"
}

问题是,我想让我的API更宽松;我希望Spring*只 * 使用POST请求主体,完全忽略Content-Type。(如果请求主体不是有效的JSON或者不能解析为LoginRequest示例,Spring已经用400 Bad Request响应,这很好。)在继续使用隐式JSON转换(通过Jackson)时,这是可能的吗?
我尝试过consumes="*"和其他类似consumes = {"text/*", "application/*"}的变体,但没有效果:如果Content-Type不是JSON,则API会继续给出415。

编辑

看起来此行为是由MappingJackson2HttpMessageConverter引起的,其文档中指出:
默认情况下,此转换器支持application/jsonapplication/*+json。可以通过设置supportedMediaTypes属性重写此设置。
我仍然不知道 * 如何确切 * 我定制,例如在一个custom Jackson2ObjectMapperBuilder...

t0ybt7op

t0ybt7op1#

我假设您使用的是Spring提供的默认MappingJackson2HttpMessageConverter
如果你想在所有的请求中都有相同的行为,一个解决方案是编写自定义的转换器,它不会在头中查找Content-Type(而是总是解析为JSON),然后配置Spring使用你的自定义转换器。同样,这将影响所有的请求,所以可能不适合所有的需求。

public class CustomerJsonHttpMessageConverter extends AbstractHttpMessageConverter<Object> {

private              ObjectMapper mapper          = new ObjectMapper();
private static final Charset      DEFAULT_CHARSET = Charset.forName("UTF-8");

public CustomerJsonHttpMessageConverter() {
    super(new MediaType("application", "json", DEFAULT_CHARSET));
}

@Override
protected Object readInternal(Class<?> clazz, HttpInputMessage inputMessage) throws IOException,
                                                                                    HttpMessageNotReadableException {
    return mapper.readValue(inputMessage.getBody(), clazz);
}

@Override
protected boolean supports(Class<?> clazz) {
    return true;
}

@Override
protected void writeInternal(Object value, HttpOutputMessage outputMessage) throws IOException,
                                                                                   HttpMessageNotWritableException {
    String json = mapper.writeValueAsString(value);
    outputMessage.getBody().write(json.getBytes());
}

}
要自定义媒体类型,

MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
    converter.setSupportedMediaTypes(
            Arrays.asList(
                    new MediaType("text", "plain"),
                    new MediaType("text", "html")
            ));
wfveoks0

wfveoks02#

对于其他任何对此感到好奇的人来说;
可以通过覆盖WebMvcConfigurerAdapter.extendMessageConverters来自定义所使用的MappingJackson2HttpMessageConverter,以允许多种mime类型。
但是,它没有按预期工作,因为application/x-www-form-urlencodedServletServerHttpRequest.getBody中进行了硬编码,以便在将其传递到MappingJackson2HttpMessageConverter之前将正文修改为URL编码(即使post数据是JSON)。
如果您真的需要这样做,那么我认为唯一的方法是在处理之前放置一个修改请求内容类型头部的Filter(并不是暗示这是一个好主意,只是在必要的情况下)。

o3imoua4

o3imoua43#

更新:小心你用这个

(This这可能是一个愚蠢的想法。)
这样做的副作用是服务器将**response Content-Type**设置为请求的Accept头中的第一个值!(例如,text/plain而不是正确的application/json。)
在注意到这一点后,我摆脱了这种定制,并解决了Spring的默认行为(如果请求没有正确的Content-Type,则响应415错误)。
原文回答:
MappingJackson2HttpMessageConverter javadocs声明:
默认情况下,此转换器支持application/jsonapplication/*+json。可以通过设置supportedMediaTypes属性重写此设置。
......这给我指出了一个非常简单的解决方案,似乎工作。在主应用程序类:

@Bean
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
    MappingJackson2HttpMessageConverter converter = 
        new MappingJackson2HttpMessageConverter(new CustomObjectMapper());
    converter.setSupportedMediaTypes(Arrays.asList(MediaType.ALL));
    return converter;
}

(CustomObjectMapper与我拥有的其他Jackson自定义相关;该构造函数参数是可选的。)
这确实会影响所有请求,但到目前为止,我在我的应用程序中没有看到任何问题。如果这成为一个问题,我可能只是将@RequestBody参数切换为String,并手动将其反序列化。

相关问题