背景:对已有界面进行操作日志记录,方便日后寻找问题。那就需要在拦截器做日志记录,由于需要获取前端请求参数,实现过程中出现以下问题。
问题:使用getInputStream()或getReader()方法获取请求参数,记录成功,但是后端控制器接收不到请求参数;
原因:HttpServletRequest以流的形式将参数发送至后端,一旦读取后,就会丢失,不会将原有参数发送至控制器。
最终前端报错400,bad request。
解决方案一:当请求头为Content-Type: application/x-www-form-urlencoded时,推荐使用getParameterMap()方法获取请求参数。
public static String getAllParameter(HttpServletRequest request){
StringBuilder sb = new StringBuilder("{");
Map<String, String[]> map = request.getParameterMap();
if(null != map) {
for (Map.Entry<String, String[]> entry : map.entrySet()) {
sb.append("\""+entry.getKey()+"\":");
sb.append("\""+entry.getValue()+"\",");
}
}else {
sb.append("请求参数为null");
}
sb.append("}");
return sb.toString();
}
解决方案二:当请求头为Content-Type: application/json时,只能使用流读取,getInputStream()或getReader()方法获取请求参数。需要重新封装,才能使用,否则会出现上述问题。
步骤一:重新封装HttpServletRequestWrapper,使得流能重复读取
public class RepeatedlyReadRequestWrapper extends HttpServletRequestWrapper {
//用于存储读取的流字节
private final byte[] body;
public RepeatedlyReadRequestWrapper(HttpServletRequest request)
throws IOException {
super(request);
body = readBytes(request.getReader(), "utf-8");
}
@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 boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener listener) {
}
@Override
public int read() throws IOException {
return bais.read();
}
};
}
private byte[] readBytes(BufferedReader br,String encoding) throws IOException{
String str = null,retStr="";
while ((str = br.readLine()) != null) {
retStr += str;
}
if (StringUtils.isNotBlank(retStr)) {
return retStr.getBytes(Charset.forName(encoding));
}
return null;
}
}
步骤二:添加拦截器,将HttpServletRequest重新封装。初始化上述重新封装HttpServletRequestWrapper。
public class RepeatedlyReadFilter implements Filter {
private static final Logger logger = LoggerFactory.getLogger(RepeatedlyReadFilter.class);
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
logger.debug("将HttpServletRequest重新封装。初始化上述重新封装HttpServletRequestWrapper。");
ServletRequest requestWrapper = null;
if (request instanceof HttpServletRequest) {
requestWrapper = new RepeatedlyReadRequestWrapper((HttpServletRequest) request);
}
if (null == requestWrapper) {
chain.doFilter(request, response);
} else {
chain.doFilter(requestWrapper, response);
}
}
@Override
public void destroy() {
}
}
步骤三:注册拦截器
@Configuration
public class WebSecurityConfig extends WebMvcConfigurerAdapter
{
@Bean
public RedisSessionInterceptor getSessionInterceptor()
{
return new RedisSessionInterceptor();
}
@Bean
public FilterRegistrationBean repeatedlyReadFilter() {
FilterRegistrationBean registration = new FilterRegistrationBean();
RepeatedlyReadFilter repeatedlyReadFilter = new RepeatedlyReadFilter();
registration.setFilter(repeatedlyReadFilter);
registration.addUrlPatterns("/*");
return registration;
}
}
到此就结束了,之后直接只用request.getInputStream()或request.getReader()
以下是将字节流转成字符串:
public static String btye2Str(HttpServletRequest request){
StringBuilder sb = new StringBuilder();
BufferedReader br = null;
try{
br = new BufferedReader(new InputStreamReader(request.getInputStream()));
String str;
while ((str = br.readLine()) != null){
sb.append(str);
}
br.close();
return sb.toString();
}catch (IOException e){
e.printStackTrace();
}finally{
if (null != br){
try{
br.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
}
有帮助的小伙伴,帮忙点个赞,蟹蟹