request.getInputStream()取值為空的問題
今天在專案中獲取request的請求資料為空,消耗了一天的時間
百度了兩篇文章解決了這個問題:原因 解決方案
- 闡述下問題:
專案是記錄請求資料及響應資料,但在獲取請求資料時使用request.getInputStream()為空,而使用
Enumeration enu=request.getParameterNames(); while(enu.hasMoreElements()){ String paraName=(String)enu.nextElement(); System.out.println(paraName+": "+request.getParameter(paraName)); }
但是獲取的值是不完整的,它將原資料前面部分作為引數key,後面部分作為引數value。
- 分析原因
經過一頓操作,發現客戶端上送的Content-Type的值為 這裡可以看下
application/x-www-form-urlencoded;charset=UTF-8
而不是
text/xml
text/plain
application/json
所以請求資料的值是以key/value形式儲存的。
若在使用request.getInputStream()前已經使用過getParameter或者@requestParam註解方式,則request.getInputStream()獲取為空。
3 解決方案
1 將post請求的Content-Type 設定為text/xml
2 通過遍歷獲取所有引數,再獲取引數值
3 避免在request.getInputStream()之前使用getParameter或者@requestParam註解方式
4 對httprequest進行修飾
定義InputStreamHttpServletRequestWrapper類繼承於HttpServletRequestWrapper對請求資料進行處理
import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.Arrays; import java.util.stream.Collectors; import javax.servlet.ReadListener; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import org.springframework.web.bind.annotation.RequestMethod; public class InputStreamHttpServletRequestWrapper extends HttpServletRequestWrapper{ private final byte[] streamBody; private static final int BUFFER_SIZE = 4096; //對請求資料判斷,getInputStream()為空的資料對key和value值進行拼接 public InputStreamHttpServletRequestWrapper(HttpServletRequest request) throws IOException { super(request); byte[] bytes = inputStream2Byte(request.getInputStream()); if (bytes.length == 0 && RequestMethod.POST.name().equals(request.getMethod())) { //從ParameterMap獲取引數,並儲存以便多次獲取 bytes = request.getParameterMap().entrySet().stream() .map(entry -> { String result; String[] value = entry.getValue(); if (value != null && value.length > 1) { result = Arrays.stream(value).map(s -> entry.getKey() + "=" + s) .collect(Collectors.joining("&")); } else { result = entry.getKey() + "=" + value[0]; } return result; }).collect(Collectors.joining("&")).getBytes(); } //System.err.println(new String(bytes)); streamBody = bytes; } private byte[] inputStream2Byte(InputStream inputStream) throws IOException { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); byte[] bytes = new byte[BUFFER_SIZE]; int length; while ((length = inputStream.read(bytes, 0, BUFFER_SIZE)) != -1) { outputStream.write(bytes, 0, length); } return outputStream.toByteArray(); } @Override public ServletInputStream getInputStream() throws IOException { ByteArrayInputStream inputStream = new ByteArrayInputStream(streamBody); return new ServletInputStream() { @Override public boolean isFinished() { return false; } @Override public boolean isReady() { return false; } @Override public void setReadListener(ReadListener listener) { } @Override public int read() throws IOException { return inputStream.read(); } }; } @Override public BufferedReader getReader() throws IOException { return new BufferedReader(new InputStreamReader(getInputStream())); } }
寫doFilterInternal類對InputStreamHttpServletRequestWrapper 進行呼叫即過濾器對請求的處理,該類繼承於OncePerRequestFilter,一次請求只調用一次
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.filter.OncePerRequestFilter;
public class InputStreamWrapperFilter extends OncePerRequestFilter{
@Override
protected void doFilterInternal(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
ServletRequest servletRequest = new InputStreamHttpServletRequestWrapper(httpServletRequest);
filterChain.doFilter(servletRequest, httpServletResponse);
}
}
最後確定過濾器呼叫的順序
在application類中
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.embedded.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.core.annotation.Order;
import com.springboot.SpringBootUtill.InputStreamWrapperFilter;
@SpringBootApplication
@Slf4j
public class Application
{
public static void main(String[] args)
{
log.info("=================開始成功=================");
SpringApplication.run(Application.class, args);
log.info("=================啟動成功=================");
}
@Bean
@Order(1)
public FilterRegistrationBean inputStreamWrapperFilterRegistration() {
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(new InputStreamWrapperFilter());
registrationBean.setName("inputStreamWrapperFilter");
registrationBean.addUrlPatterns("/*");
return registrationBean;
}
}
到此為止該問題已經解決了,可以獲取到引數了
//獲取請求body
String getRequestMsg(HttpServletRequest request) throws IOException {
BufferedReader br = request.getReader();
String str, wholeStr = "";
while((str = br.readLine()) != null){
wholeStr += str;
}
return wholeStr;
}
現在還有個疑問,我並沒有在request.getInputStream()前使用過getParameter或者@requestParam註解方式。是不是springboot在嵌入tomcat後,請求自動呼叫了getParameter方法呢。