我使用最新的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 ...
答案 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-urlencoded
在ServletServerHttpRequest.getBody
中被硬编码以修改要编码的主体(即使帖子数据是JSON),然后再将其传递给{{1} }。
如果您真的需要这个工作,那么我认为唯一的方法是在处理之前设置一个MappingJackson2HttpMessageConverter
来修改请求内容类型标头(并不意味着这是一个好主意,只要情况出现)这是必要的。)
答案 2 :(得分:-1)
(无论如何,这可能是一个愚蠢的想法。)
这会产生副作用,即服务器将响应Content-Type
设置为请求的Accept
标头中的第一个值! (例如text/plain
而不是正确的application/json
。)
在注意到之后,我摆脱了这种自定义并解决了Spring的默认行为(如果请求没有正确的Content-Type
,则回复415错误。)
原始答案:
MappingJackson2HttpMessageConverter javadocs声明:
默认情况下,此转换器支持
application/json
和application/*+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,并手动反序列化。