最近在工做中遇到的一个需求,将请求中的客户端类型、操做系统类型、ip、port、请求方式、URI以及请求参数值收集到日志中,网上找资料说用拦截器拦截全部请求而后收集信息,因而就开始了操做:前端
试了以后发现当请求方式为POST,前端发送数据json时只能用request.getReader()流获取,自信满满从流中获取以后发现请求以后报错:java
getInputStream() has already been called for this request...
因而网上找答案,发现是ServletRequest的getReader()和getInputStream()两个方法只能被调用一次,并且不能两个都调用。那么若是Filter中调用了一次,在Controller里面就不能再调用了。web
而后又开始找解决方法,说既然ServletInputStream不支持从新读写,就把流读出来后用容器存储起来,后面就能够屡次利用了。spring
因而继承 HttpServletRequestWrapper类(http请求包装器,其基于装饰者模式实现了HttpServletRequest界面)并实现想要从新定义的方法以达到包装原生HttpServletRequest对象。还须要在过滤器里将原生的HttpServletRequest对象替换成咱们的RequestWrapper对象。apache
测试发现POST请求参数值能够在拦截器类中获取到了,本觉得大功告成,又发现GET请求很差使了,开始报错Stream closed,一顿操做发现须要在过滤器进行判断,若是是POST请求走本身的继承的HttpServletRequestWrapper类请求,不然走普通的请求。终于成功!忽然舒服了。json
<dependency> <groupId>eu.bitwalker</groupId> <artifactId>UserAgentUtils</artifactId> <version>1.21</version> </dependency>
package com.btrc.access.util; import javax.servlet.http.HttpServletRequest; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.nio.charset.Charset; public class RequestUtil { public static String getBodyString(HttpServletRequest request) { StringBuilder sb = new StringBuilder(); try ( InputStream inputStream = request.getInputStream(); BufferedReader 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(); } return sb.toString(); } }
package com.btrc.access.filter; import com.btrc.access.util.RequestUtil; import eu.bitwalker.useragentutils.UserAgent; import org.apache.commons.lang.StringUtils; import org.springframework.http.HttpMethod; import org.springframework.web.servlet.HandlerInterceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * 请求拦截器:拦截请求目的是将请求的信息收集到日志 */ public class RequestInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { UserAgent userAgent = UserAgent.parseUserAgentString(request.getHeader("user-agent")); //客户端类型 String clientType = userAgent.getOperatingSystem().getDeviceType().getName(); //客户端操做系统类型 String osType = userAgent.getOperatingSystem().getName(); //客户端ip String clientIp = request.getRemoteAddr(); //客户端port int clientPort = request.getRemotePort(); //请求方式 String requestMethod = request.getMethod(); //客户端请求URI String requestURI = request.getRequestURI(); //客户端请求参数值 String requestParam; //若是请求是POST获取body字符串,不然GET的话用request.getQueryString()获取参数值 if(StringUtils.equalsIgnoreCase(HttpMethod.POST.name(), requestMethod)){ requestParam = RequestUtil.getBodyString(request); }else{ requestParam = request.getQueryString(); } //客户端总体请求信息 StringBuilder clientInfo = new StringBuilder(); clientInfo.append("客户端信息:[类型:").append(clientType) .append(", 操做系统类型:").append(osType) .append(", ip:").append(clientIp) .append(", port:").append(clientPort) .append(", 请求方式:").append(requestMethod) .append(", URI:").append(requestURI) .append(", 请求参数值:").append(requestParam.replaceAll("\\s*", "")) .append("]"); //***这里的clientInfo就是全部信息了,请根据本身的日志框架进行收集*** System.out.println(clientInfo); //返回ture才会继续执行,不然一直拦截住 return true; } }
package com.btrc.access.filter; import com.btrc.access.util.RequestUtil; import javax.servlet.ReadListener; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import java.io.*; import java.nio.charset.Charset; public class AccessRequestWrapper extends HttpServletRequestWrapper { private final byte[] body; public AccessRequestWrapper(HttpServletRequest request) { super(request); body = RequestUtil.getBodyString(request).getBytes(Charset.forName("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 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.btrc.access.filter; import org.apache.commons.lang.StringUtils; import org.springframework.http.HttpMethod; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; public class AccessFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) servletRequest; //若是是POST走本身的继承的HttpServletRequestWrapper类请求,不然走正常的请求 if(StringUtils.equalsIgnoreCase(HttpMethod.POST.name(), request.getMethod())){ //必定要在判断中new对象,不然还会出现Stream closed问题 filterChain.doFilter(new AccessRequestWrapper(request),servletResponse); }else{ filterChain.doFilter(servletRequest,servletResponse); } } @Override public void destroy() { } }
package com.btrc.access.config; import com.btrc.access.filter.AccessFilter; import com.btrc.access.filter.RequestInterceptor; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import javax.servlet.Filter; /** * 拦截器过滤器配置类 */ @Configuration public class WebMvcConfig implements WebMvcConfigurer { @Bean public FilterRegistrationBean httpServletRequestReplacedFilter() { FilterRegistrationBean registration = new FilterRegistrationBean(); registration.setFilter(new AccessFilter()); // /* 是所有的请求拦截,和Interceptor的拦截地址/**区别开 registration.addUrlPatterns("/*"); registration.setName("accessRequestFilter"); registration.setOrder(1); return registration; } @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new RequestInterceptor()).addPathPatterns("/**"); } }