从Spring的休息控制器同时支持application / json和application / x-www-form-urlencoded

时间:2017-12-21 11:54:26

标签: spring spring-restcontroller

我正在编写一个REST端点,它需要同时支持application / x-www-form-urlencoded和application / json作为请求体。我已经做了以下配置,

@RequestMapping(method = RequestMethod.POST, produces = { MediaType.APPLICATION_JSON_VALUE }, consumes = {          
        MediaType.APPLICATION_FORM_URLENCODED_VALUE, MediaType.APPLICATION_JSON_VALUE }, path = Constants.ACCESS_TOKEN_V1_ENDPOINT)
public OAuth2Authorization createAccessTokenPost(
        @RequestBody(required = false) MultiValueMap<String, String> paramMap) { ..

虽然它单独支持application / x-www-form-urlencoded或application / json(当我从consumes = {}注释掉一个内容类型时),但它不同时支持两者。有什么想法吗?

3 个答案:

答案 0 :(得分:1)

根据我的调查结果,spring不支持内容类型“application/x-www-form-urlencoded”,“application/json”和“application/xml”。

我认为原因:Spring通过解析并将它们注入标有@RequestBody spring注释的java pojo来处理JSON和XML类型。但是,x-www-form-urlencoded必须注入标有MultiValueMap<>的{​​{1}}对象。标记为@RequestBody的两种不同的Java类型将不会同时支持,因为spring可能不知道注入有效负载的位置。

工作解决方案

@RequestBody”可以支持,因为它在API中。也就是说,可以使用@RequestBody注释将其注入spring application/x-www-form-urlencoded

为了在同一方法上支持JSON和XML,我们可以利用servlet规范和构建在它们之上的spring类来提取有效负载作为流。

示例代码

MultiValueMap<>

注意事项使用此方法可以支持任何自定义数据类型/标记/格式。 Spring的import org.springframework.http.HttpInputMessage; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter; import org.springframework.http.server.ServletServerHttpRequest; import org.springframework.util.MultiValueMap; // usual REST service class @Autowired private MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter; @Autowired private Jaxb2RootElementHttpMessageConverter jaxb2RootElementHttpMessageConverter; public ResponseEntity<Object> authorizationRequestPost(HttpServletResponse response, HttpServletRequest request,@RequestBody(required = false) MultiValueMap<String, String> parameters) { // this MultiValueMap<String,String> will contain key value pairs of "application/x-www-form-urlencoded" parameters. // payload object to be populated Authorization authorization = null; HttpInputMessage inputMessage = new ServletServerHttpRequest(request) { @Override public InputStream getBody() throws IOException { return request.getInputStream(); } }; if (request.getContentType().equals(MediaType.APPLICATION_JSON_VALUE)) { authorization = (Authorization) mappingJackson2HttpMessageConverter.read(Authorization.class, inputMessage); } else if (request.getContentType().equals(MediaType.APPLICATION_XML_VALUE)) { authorization = (Authorization)jaxb2RootElementHttpMessageConverter.read(Authorization.class, inputMessage); } else{ // extract values from MultiValueMap<String,String> and populate Authorization } // remaining method instructions } 可以扩展为编写解析逻辑。

另一种可能的方法可以是一个执行相同逻辑的AOP样式解决方案:通过从org.springframework.http.converter.HttpMessageConverter<>输入流中提取有效负载来解析有效负载并注入到有效负载对象中。

第三种方法将编写一个用于执行逻辑的过滤器。

答案 1 :(得分:0)

使用单个Spring控制器方法无法同时处理application/jsonapplication/x-www-form-urlencoded请求。

Spring通过ServletRequest.getParameter(java.lang.String)获得了application/x-www-form-urlencoded数据,文件说:

  

对于HTTP servlet,参数包含在查询字符串或发布的表单数据中。

     

如果参数数据是在请求正文中发送的(例如HTTP POST请求中发生的情况),则直接通过getInputStream()或getReader()读取正文会干扰此方法的执行。

因此,如果您的方法参数用@RequestBody注释,Spring将读取请求正文并将其解析为方法参数对象。但是application/x-www-form-urlencoded导致Spring通过调用ServletRequest.getParameter(java.lang.String)来填充参数对象。

答案 2 :(得分:0)

要做到这一点,即使您不使用ProductTags注释MultiValueMap,上面的answer也不起作用,它将始终检查@RequestBody其余情况解决为contentType==MediaType.APPLICATION_FORM_URLENCODED_VALUE