问题:平台升级Spring3.1.2.RELEASE升级为5.1.12.RELEASE。升级后controller层无法获取request中的数据流。
背景: ajax请求
请求格式未设置为默认的 application/x-www-form-urlencoded; charset=UTF-8
请求参数格式 xml
请求方式为 post
后台代码:
public static String getDataFromRequest(HttpServletRequest request) throws IOException{
//获取请求流,并转为字符串,使用DOM4J解析该XML
ServletInputStream postQueryStream = request.getInputStream();
//BUFFER_SIZE 为1024
byte[] buffer = new byte[BUFFER_SIZE];
int length = 0;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
do {
length = postQueryStream.read(buffer);
if (length > 0) {
baos.write(buffer, 0, length);
}
} while (length>0);
String strQueryText = new String(baos.toByteArray(), ENCODING);
return strQueryText;
}
length的返回值为-1,无法读取request中的数据流。但是request.getParameter()可以获取数据信息。
首先看一下servlet规范
由上图可得 当满足四个条件后,无法通过request.getInputStream()获取数据流。此时已经满足了前三个条件
下面看一下升级前后关键代码部分对比 (注意代码中注释部分)
Spring5.1.12版本
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
this.logRequest(request);
Map<String, Object> attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
attributesSnapshot = new HashMap();
Enumeration attrNames = request.getAttributeNames();
//略...
private void logRequest(HttpServletRequest request) {
LogFormatUtils.traceDebug(this.logger, (traceOn) -> {
String params;
if (this.isEnableLoggingRequestDetails()) {
params = (String)request.getParameterMap().entrySet().stream().map((entry) -> {
return (String)entry.getKey() + ":" + Arrays.toString((Object[])entry.getValue());
}).collect(Collectors.joining(", "));
} else {
//此时触发上图中的第四个条件,servlet调用了getParameter系列方法
params = request.getParameterMap().isEmpty() ? "" : "masked";
}
String queryString = request.getQueryString();
String queryClause = StringUtils.hasLength(queryString) ? "?" + queryString : "";
//略
Spring3.1.2版本(代码中没有getParameter类似方法)
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
if (logger.isDebugEnabled()) {
String requestUri = urlPathHelper.getRequestUri(request);
logger.debug("DispatcherServlet with name '" + getServletName() + "' processing " + request.getMethod() +
" request for [" + requestUri + "]");
}
// Keep a snapshot of the request attributes in case of an include,
// to be able to restore the original attributes after the include.
Map<String, Object> attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
logger.debug("Taking snapshot of request attributes before include");
attributesSnapshot = new HashMap<String, Object>();
Enumeration<?> attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
}
// Make framework objects available to handlers and view objects.
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
if (inputFlashMap != null) {
request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
}
request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
try {
doDispatch(request, response);
}
finally {
// Restore the original attribute snapshot, in case of an include.
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
由此可见在满足前三个条件的情况下,Spring3版本没有request.getParameter()系列方法,没有满足第四个条件所以升级之前可以通过request.getInputStream()获取数据流。而Spring5版本中存在request.getParameter()系列方,四个条件全部满足,所以无法获取数据流。
修改:设置ajax请求内容格式
contentType: 'text/xml;charset=UTF-8'
修改方法:使请求不满足第三个条件。可根据实际情况,对四个条件进行修改。