今天出现这样一个问题:
现在开发的项目是基于SpringBoot的maven项目,有个需求就是要加一个拦截器和过滤器,在拦截器中我需要获取到前端传过来的json数据,按照常理来说,获取请求参数使用request.getParameter()方法就可以,但是不知道为什么在这个项目里面获取不到。这时候我想到了使用流的方法从request对象中直接读出来。
当然,方法写好后,在拦截器中也读取到了请求参数,但是出现一个问题。就是后面的controller中使用了@RequestBody注解获取参数,但是拦截器执行过后,controller获取不到参数,甚至是方法都没有进入对应的方法中就报错了(注:拦截器中不使用流,controller中是能获取到参数的)。
在debug过程中,发现拦截器使用流和不适用流到了一个获取字符集解析器的方法里面就有差别了。一个是能获取到字符集解析器。另一个获取不到。但是看了执行过程也没发现什么问题。就百度了一下,发现了Spring有一个问题就是: ServletRequest中getReader()和getInputStream()只能调用一次。而又由于@RequestBody注解获取输出参数的方式也是根据流的方式获取的。所以我们前面使用流获取后,后面的@RequestBody就获取不到对应的输入流了。
那么问题找到之后。想着解决。从网上查找了一下,有一个方法就是。先读取流,然后在将流写进去就行了,下面是我的一些解决代码:
过滤器:
package com.***;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
/**
* Created by yefuliang on 2017/10/25.
*/
@WebFilter(filterName="myFilter",urlPatterns="/*")
public class MyFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("--------------过滤器初始化------------");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("--------------执行过滤操作------------");
// 防止流读取一次后就没有了, 所以需要将流继续写出去
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
ServletRequest requestWrapper = new BodyReaderHttpServletRequestWrapper(httpServletRequest);
filterChain.doFilter(requestWrapper, servletResponse);
}
@Override
public void destroy() {
System.out.println("--------------过滤器销毁------------");
}
}
在代码中我们可以看到我们为了方式流的丢失,我们新建了一个类的对象,注意filterChain.doFilter(requestWrapper, servletResponse);这句代码的requestWrapper参数,和原生的不一致哦,是我们处理过后的ServletRequest。下面是BodyReaderHttpServletRequestWrapper的实现:
package com.***;
/**
* Created by yefuliang on 2017/10/25.
*/
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
import java.nio.charset.Charset;
/**
* 保存流
*
* @author yefuliang 2017年10月25日
*/
public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper {
private final byte[] body;
public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
String sessionStream = getBodyString(request);
body = sessionStream.getBytes(Charset.forName("UTF-8"));
}
/**
* 获取请求Body
*
* @param request
* @return
*/
public String getBodyString(final ServletRequest request) {
StringBuilder sb = new StringBuilder();
InputStream inputStream = null;
BufferedReader reader = null;
try {
inputStream = cloneInputStream(request.getInputStream());
reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")));
String line = "";
while ((line = reader.readLine()) != null) {
sb.append(line);
}
}
catch (IOException e) {
e.printStackTrace();
}
finally {
if (inputStream != null) {
try {
inputStream.close();
}
catch (IOException e) {
e.printStackTrace();
}
}
if (reader != null) {
try {
reader.close();
}
catch (IOException e) {
e.printStackTrace();
}
}
}
return sb.toString();
}
/**
* Description: 复制输入流
*
* @param inputStream
* @return
*/
public InputStream cloneInputStream(ServletInputStream inputStream) {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len;
try {
while ((len = inputStream.read(buffer)) > -1) {
byteArrayOutputStream.write(buffer, 0, len);
}
byteArrayOutputStream.flush();
}
catch (IOException e) {
e.printStackTrace();
}
InputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
return byteArrayInputStream;
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream()));
}
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream bais = new ByteArrayInputStream(body);
return new ServletInputStream() {
@Override
public int read() throws IOException {
return bais.read();
}
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
};
}
}
代码逻辑大家可以自己看下,很简单的一个逻辑。
下面是我的拦截器获取参数代码:
package com.***;
import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.UUID;
/**
* 自定义拦截器
* 拦截时机 Filter pre -> service -> dispatcher -> preHandle ->controller
* ->postHandle - > afterCompletion -> FilterAfter
* Created by yefuliang on 2017/10/23.
*/
public class bgqCommonInterceptorl implements HandlerInterceptor {
/**
* 在controller处理之前首先对请求参数进行处理,以及对公共参数的保存
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("---------------拦截器开始------------------");
try{
response.setHeader("Content-type", "application/json;charset=UTF-8");
String requestMethord = request.getRequestURI();//请求方法
if(requestMethord==null){
return false;
}
//获取请求参数
JSONObject parameterMap = JSON.parseObject(new BodyReaderHttpServletRequestWrapper(request).getBodyString(request));
String dataFrom = String.valueOf(parameterMap.get("dataFrom"));
}catch (Exception e){
e.printStackTrace();
}
System.out.println("拦截器拦截完成");
return true;
}
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
System.out.println("---------------拦截器方法二开始------------------");
}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
System.out.println("---------------拦截器方法三开始------------------");
}
parameterMap 就是获取的请求参数json对象。
问题解决。