如何使@RestController POST方法忽略Content-Type标头并仅使用请求体?

时间:2015-02-13 16:27:20

标签: spring spring-mvc jackson spring-boot

我使用最新的Spring 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/json和   application/*+json。这可以通过设置supportedMediaTypes属性来覆盖。

我仍然缺少我如何自定义,例如在 custom Jackson2ObjectMapperBuilder ...

3 个答案:

答案 0 :(得分:0)

我假设您使用的是Spring提供的默认MappingJackson2HttpMessageConverter

如果您希望在所有请求中具有相同的行为,一种解决方案是编写自定义转换器,它不会在标头中查找Content-Type(而是将解析为JSON alwayse)然后配置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")
            ));

答案 1 :(得分:0)

对于其他对此感到好奇的人;

可以通过覆盖MappingJackson2HttpMessageConverter来自定义使用的WebMvcConfigurerAdapter.extendMessageConverters以允许多种mime类型。

但是,它没有按预期工作,因为application/x-www-form-urlencodedServletServerHttpRequest.getBody中被硬编码以修改要编码的主体(即使帖子数据是JSON),然后再将其传递给{{1} }。

如果您真的需要这个工作,那么我认为唯一的方法是在处理之前设置一个MappingJackson2HttpMessageConverter来修改请求内容类型标头(并不意味着这是一个好主意,只要情况出现)这是必要的。)

答案 2 :(得分:-1)

更新:注意您是否使用此

(无论如何,这可能是一个愚蠢的想法。)

这会产生副作用,即服务器将响应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 customisations相关;该contructor参数是可选的。)

这确实会影响所有请求,但到目前为止,我在我的应用中看不出有任何问题。如果这成为一个问题,我可能只是将@RequestBody参数切换为String,并手动反序列化。