spring MVC 获取request中的body体

来源:互联网 发布:淘宝异地客服兼职 编辑:程序博客网 时间:2024/06/05 11:27

<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"><span style="white-space:pre"></span>在项目中,客户端向服务端传送一组json数据,这组数据随着时间的推移可能会越来越大,可能会受到服务器对参数大小的限制的影响,因此,想直接将数据塞进request的body体中,继而发送给服务端,服务端直接将request中body 以流的方式读出来,并持久化。</span>

HttpServletRequest类中有两个方法 getInputStream() 和request.getReader() ,本以为可以直接快速搞定,但是运行程序时报异常:getInputStream() has already been called for this request ,经过查证,原来项目采用springmvc框架,框架内部已经读过request中流,所以当再次读取时,就读不到了,所以直接获取走不通了。

在网上问了问度娘,springmvc 中有一个注解@RequestBody 可以获取request中数据,部分代码如下:

@RequestMapping("/save")

public ModelAndView save(HttpServletRequest request,@RequestBody String s){ 

//具体操作 省略

}

运行过程中,还是有问题,但至少有进了一步,上述的String对象依然无法获取数据,这就有些纠结了,又开始问度娘和下载找spring的源码,了解@RequestBody的实现细节,发现获取body spring用了一系列的HttpMessageConverter 来实现,如:ByteArrayHttpMessageConverter,stringHttpMessageConverter ,spring自个实现的转换器,com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter【国产的json转换器,该转换器需要在配置文件中配置】,默认的转换器的初始化工作在RequestMappingHandlerAdapter这个类中实现

private List<HttpMessageConverter<?>> messageConverters;//转换器的集合
public RequestMappingHandlerAdapter() {StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();stringHttpMessageConverter.setWriteAcceptCharset(false); // See SPR-7316this.messageConverters = new ArrayList<HttpMessageConverter<?>>();this.messageConverters.add(new ByteArrayHttpMessageConverter());this.messageConverters.add(stringHttpMessageConverter);this.messageConverters.add(new SourceHttpMessageConverter<Source>());this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());}//初始化工作

找到这发现byte ,String的转换器已经初始化了,但是为啥还是拿不到String类型的数据呢,接着找关于converters相关的代码,有个setMessageConverters方法

/** * Provide the converters to use in argument resolvers and return value * handlers that support reading and/or writing to the body of the * request and response. */public void setMessageConverters(List<HttpMessageConverter<?>> messageConverters) {this.messageConverters = messageConverters;}
spring在解析xml过程中会调用该方法 ,从而将用到的converters注入进去,因此明白项目在启动的过程中重新设置了HttpMessageConverter ,而我需要的converter并没有注入进去,因此在xml中配置如下

<mvc:annotation-driven ><mvc:message-converters>             <ref bean="fastJsonHttpMessageConverter" />         </mvc:message-converters> </mvc:annotation-driven><bean id="stringHttpMessageConverter"          class="org.springframework.http.converter.StringHttpMessageConverter">          <constructor-arg value="UTF-8" index="0"></constructor-arg>        <property name="supportedMediaTypes">              <list>                  <value>text/plain;charset=UTF-8</value>              </list>          </property>      </bean>    <bean id="byteHttpMessageConverter"          class="org.springframework.http.converter.ByteArrayHttpMessageConverter">      </bean>        <bean id="fastJsonHttpMessageConverter"          class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">            <property name="supportedMediaTypes">              <list>                  <value>application/json;charset=UTF-8</value>                  <value>text/html;charset=UTF-8</value>             </list>          </property>         <property name="features">              <list>                  <!-- <value>WriteMapNullValue</value> -->                  <value>QuoteFieldNames</value>                  <value>WriteDateUseDateFormat</value>              </list>          </property>        </bean>  

这时启动项目,可以拿到数据了。。。

因此,在遇到问题的时候,先按照自己的思路尝试着去解决,可能有时候有些问题确实不知道如何解决的时候,可以先查查网上有没有相关问题的解决方案,选择合适的方案,自然问题就迎刃而解了,但是个别的时候,会遇到相当棘手的问题,通过一系列方案都无法完美解决的时候,可以尝试去查阅源码,同过阅读源码,一会理解框架的开发者的设计思路,只要顺着他们的思路,我们的问题自然就不是问题了。

最后,多读读优秀的开源项目的源码。


附:遍历转换器的代码

AbstractMessageConverterMethodArgumentResolver 


/** * Creates the method argument value of the expected parameter type by reading * from the given HttpInputMessage. * * @param <T> the expected type of the argument value to be created * @param inputMessage the HTTP input message representing the current request * @param methodParam the method argument * @param targetType the type of object to create, not necessarily the same as * the method parameter type (e.g. for {@code HttpEntity<String>} method * parameter the target type is String) * @return the created method argument value * @throws IOException if the reading from the request fails * @throws HttpMediaTypeNotSupportedException if no suitable message converter is found */@SuppressWarnings("unchecked")protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage,MethodParameter methodParam, Type targetType) throws IOException, HttpMediaTypeNotSupportedException {MediaType contentType = inputMessage.getHeaders().getContentType();if (contentType == null) {contentType = MediaType.APPLICATION_OCTET_STREAM;}Class<?> contextClass = methodParam.getDeclaringClass();Map<TypeVariable, Type> map = GenericTypeResolver.getTypeVariableMap(contextClass);Class<T> targetClass = (Class<T>) GenericTypeResolver.resolveType(targetType, map);for (HttpMessageConverter<?> converter : this.messageConverters) {if (converter instanceof GenericHttpMessageConverter) {GenericHttpMessageConverter genericConverter = (GenericHttpMessageConverter) converter;if (genericConverter.canRead(targetType, contextClass, contentType)) {if (logger.isDebugEnabled()) {logger.debug("Reading [" + targetType + "] as \"" +contentType + "\" using [" + converter + "]");}return genericConverter.read(targetType, contextClass, inputMessage);}}if (targetClass != null) {if (converter.canRead(targetClass, contentType)) {if (logger.isDebugEnabled()) {logger.debug("Reading [" + targetClass.getName() + "] as \"" +contentType + "\" using [" + converter + "]");}return ((HttpMessageConverter<T>) converter).read(targetClass, inputMessage);}}}throw new HttpMediaTypeNotSupportedException(contentType, allSupportedMediaTypes);}












2 0
原创粉丝点击