Spring 如何使@RestController POST方法忽略内容类型头并仅使用请求体?
我使用的是最新的SpringBoot(1.2.1)和它附带的任何SpringMVC版本 我有一个控制器方法,用于传入和传出数据的隐式JSON转换:Spring 如何使@RestController POST方法忽略内容类型头并仅使用请求体?,spring,spring-mvc,jackson,spring-boot,Spring,Spring Mvc,Jackson,Spring Boot,我使用的是最新的SpringBoot(1.2.1)和它附带的任何SpringMVC版本 我有一个控制器方法,用于传入和传出数据的隐式JSON转换: @RestController public class LoginController { @RequestMapping(value = "/login", method = POST, produces = "application/json") ResponseEntity<Lo
@RestController
public class LoginController {
@RequestMapping(value = "/login", method = POST, produces = "application/json")
ResponseEntity<LoginResponse> login(@RequestBody LoginRequest loginRequest) {
// ...
}
}
问题是,我想让我的API更加宽松;我希望Spring只使用POST请求主体,完全忽略内容类型
标题。(如果请求体不是有效的JSON,或者无法解析为LoginRequest实例,Spring已经用400个错误的请求进行响应,这很好。)在继续使用隐式JSON转换(通过Jackson)的同时,这是否可能
我尝试了consumes=“*”
,以及其他变体,如consumes={“text/*”,“application/*”}
,但没有效果:如果内容类型
不是JSON,API会一直给出415
编辑
这种行为似乎是由以下文件引起的:
默认情况下,此转换器支持application/json
和
application/*+json
。这可以通过设置supportedMediaTypes
属性来覆盖
我仍然不知道如何准确地定制它,例如在
…我假设您使用的是Spring提供的默认
映射Jackson2HttpMessageConverter
如果您希望在所有请求中都有相同的行为,一个解决方案是在头中编写自定义转换器,它不会查找内容类型
(而是解析为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());
}
更新:如果你使用这个,请小心
(无论如何,这可能是个愚蠢的想法。)
这有一个副作用,服务器将响应内容类型设置为请求的接受头中的第一个值!(例如,text/plain
而不是正确的应用程序/json
)
注意到这一点后,我放弃了这种定制,并决定使用Spring的默认行为(如果请求没有正确的内容类型,则响应415错误)
原始答复:
声明:
默认情况下,此转换器支持application/json
和application/*+json
。这可以通过设置supportedMediaTypes
属性来覆盖
…这让我找到了一个看似可行的简单解决方案。在主应用程序类中:
@Bean
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
MappingJackson2HttpMessageConverter converter =
new MappingJackson2HttpMessageConverter(new CustomObjectMapper());
converter.setSupportedMediaTypes(Arrays.asList(MediaType.ALL));
return converter;
}
(CustomObjectMapper与我拥有的其他参数相关;该构造函数参数是可选的。)
这确实会影响所有的请求,但到目前为止,我在我的应用程序中没有发现这方面的问题。如果这成为一个问题,我可能会将@RequestBody
参数切换为字符串,并手动对其进行反序列化
通过覆盖WebMVCConfigureAdapter.extendMessageConverters
以允许多种mime类型,可以自定义使用的MappingJackson2HttpMessageConverter
但是,它不能像预期的那样工作,因为application/x-www-form-urlencoded
是硬编码的,在将正文传递给MappingJackson2HttpMessageConverter
之前修改为url编码(即使post数据是JSON)
如果您真的需要这样做,那么我认为唯一的方法是在处理之前放置一个修改请求内容类型头的过滤器(这并不意味着这是一个好主意,只是在需要这样做的情况下)。您是否尝试将参数类型从LoginRequest更改为String?Spring应该如何从文本/普通请求转换为对象?JSON字符串和(model/DTO)对象之间的隐式转换是Spring MVC的标准功能。我不知道具体是怎么做的,但它就是这么做的。相关:(他的javadoc实际上给了我一个关于谁来做这件事的线索)。当有效负载是application/json时,您提到的转换器就会启动。当您将有效负载指定为text/plain时,您应该期望将有效负载指定为字符串。请注意,我的问题是:“在继续使用隐式JSON转换时这可能吗?”将参数设置为字符串始终是一个备份计划。但我似乎是按照javadoc的建议来做的:对不起,你是对的,我错过了那部分。顺便问一下,您能解释一下从非JSON内容类型接受和解码JSON负载的原因吗?您正在处理旧客户端吗?我猜这应该是可行的,而不必覆盖readInternal()
和writeInternal()
(我真的不想定制)。通过以某种方式自定义支持的媒体类型
。。。如果你真的需要忽略内容类型,我认为这是唯一的方法——编写自定义转换器。如果您只需要接受其他内容类型,那么使用consumes
就足够了。奇怪的是,它没有工作,你能验证你的spring版本吗<代码>消耗
来自Spring3.1.*
。此外,您可以尝试相对较旧的-headers=“Content-type:”
语法。对于自定义媒体类型,我编辑了我的答案,可能不值得那么麻烦(因为我当然可以手动反序列化JSON字符串)。如前所述,我使用Spring Boot 1.2.1和。如何使Spring使用定制的MappingJackson2HttpMessageConverter?我尝试在我的主应用程序中定义一个@Bean
工厂方法,但奇怪的是,由于“无法实例化XmlMapper-在类路径上找不到”检查此链接,该方法失败了。它配置ObjectMapper,因此您可以对媒体类型执行此操作。您将如何在筛选器中修改它?请求是不可变的。
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setSupportedMediaTypes(
Arrays.asList(
new MediaType("text", "plain"),
new MediaType("text", "html")
));
@Bean
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
MappingJackson2HttpMessageConverter converter =
new MappingJackson2HttpMessageConverter(new CustomObjectMapper());
converter.setSupportedMediaTypes(Arrays.asList(MediaType.ALL));
return converter;
}