在Spring MVC控制器中反序列化单属性JSON有效负载


问题内容

我想创建在语义上如下所示的控制器方法

public HttpEntity<?> deleteUser(String userId){
...
}

客户端将传递用户ID作为JSON有效负载的一部分。如果我尝试注释@RequestBody字符串参数并发出{"userId":"foo"}有效负载,则会出现异常

Caused by: com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of java.lang.String out of START_OBJECT token
 at [Source: java.io.PushbackInputStream@7311a203; line: 1, column: 1]
    at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:148) ~[jackson-databind-2.6.1.jar:2.6.1]
    at com.fasterxml.jackson.databind.DeserializationContext.mappingException(DeserializationContext.java:854) ~[jackson-databind-2.6.1.jar:2.6.1]
    at com.fasterxml.jackson.databind.deser.std.StringDeserializer.deserialize(StringDeserializer.java:62) ~[jackson-databind-2.6.1.jar:2.6.1]
    at com.fasterxml.jackson.databind.deser.std.StringDeserializer.deserialize(StringDeserializer.java:11) ~[jackson-databind-2.6.1.jar:2.6.1]
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3702) ~[jackson-databind-2.6.1.jar:2.6.1]
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2798) ~[jackson-databind-2.6.1.jar:2.6.1]
    at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:221) ~[spring-web-4.2.0.RELEASE.jar:4.2.0.RELEASE]

这是合理的,因为JSON希望将复杂的对象(即具有一个属性)反序列化为String

我也知道这"foo"是无效的JSON。而且我知道我 可以 使用a Map<String,Object>甚至更好的a
ModelMap,并且作为最后的选择,我可以使用查询字符串and @RequestParam但是
今天老板明确要求我找到一种使用纯字符串而不是对象的方法,为了使代码看起来更具可读性。

如何强制Jackson / MVC仅将“ username”属性反序列化为普通格式String


问题答案:

当Spring MVC找到与URL路径匹配的请求映射,但参数(或标头或其他内容)与处理程序方法期望的不匹配时,通常会看到这种类型的错误。

如果使用@RequestBody批注,则Spring MVC希望将POST请求的整个主体映射到Object,默认情况下,它不使用String。

可以采用以下几种不同的方法:

1) 将deleteUser()方法类型的方法类型更改为GET而不是Post,并使用userId作为String。

2) 您只需将HttpServletRequest注入您的方法并读取正文:

public void deleteUser(HttpServletRequest request) {
  String userID = IOUtils.toString( request.getInputStream());
  // do stuff
}

3) 使用可以替换String参数的包装器(JSON对象的Java模型),这也可以与帖子中的json一起正常工作。

public class UserWrapper {

    private String userId;
    //getter setters

然后在您的控制器中用作:

public void deleteUser(@RequestBody UserWrapper user) {
//do your stuff
}

4) Spring提供了一种配置多个消息转换器的方法,如下所示: 注意: 然后,对各种方法的请求必须使用适当的值指定“ content-
type”标头。对于那些将请求主体映射到JAXB bean的方法,请指定“ application /
xml”。对于那些请求主体是字符串的用户,请使用“文本/纯文本”。

<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
    <property name="messageConverters">
        <list>
            <ref bean="jsonConverter" />
            <ref bean="marshallingConverter" />
            <ref bean="stringHttpMessageConverter" />
        </list>
    </property>
</bean>

<bean id="jsonConverter"
      class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
    <property name="supportedMediaTypes" value="application/json" />
</bean>

<bean id="marshallingConverter"
      class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter">
    <constructor-arg ref="jaxb2Marshaller" />
    <property name="supportedMediaTypes" value="application/xml"/>
</bean>

<bean id="stringHttpMessageConverter"
      class="org.springframework.http.converter.StringHttpMessageConverter">
    <property name="supportedMediaTypes" value="text/plain"/>
</bean>

希望这对您有所帮助!