最近在工作中遇到的一个需求,将请求中的客户端类型、操作系统类型、ip、port、请求方式、URI以及请求参数值收集到日志中,网上找资料说用拦截器拦截所有请求然后收集信息,于是就开始了操作:
2、问题试了之后发现当请求方式为POST,前端发送数据json时只能用request.getReader()流获取,自信满满从流中获取之后发现请求之后报错:
getInputStream() has already been called for this request...于是网上找答案,发现是ServletRequest的getReader()和getInputStream()两个方法只能被调用一次,而且不能两个都调用。那么如果Filter中调用了一次,在Controller里面就不能再调用了。
然后又开始找解决方法,说既然ServletInputStream不支持重新读写,就把流读出来后用容器存储起来,后面就可以多次利用了。
于是继承 HttpServletRequestWrapper类(http请求包装器,其基于装饰者模式实现了HttpServletRequest界面)并实现想要重新定义的方法以达到包装原生HttpServletRequest对象。还需要在过滤器里将原生的HttpServletRequest对象替换成我们的RequestWrapper对象。
测试发现POST请求参数值可以在拦截器类中获取到了,本以为大功告成,又发现GET请求不好使了,开始报错Stream closed,一顿操作发现需要在过滤器进行判断,如果是POST请求走自己的继承的HttpServletRequestWrapper类请求,否则走普通的请求。终于成功!突然舒服了。
2、获取 1)导入依赖为了获取客户端类型、操作系统类型、ip、port <dependency> <groupId>eu.bitwalker</groupId> <artifactId>UserAgentUtils</artifactId> <version>1.21</version> </dependency> 2)封装获取body字符串的工具类 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(); } } 3)拦截器类 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; } } 4)继承 HttpServletRequestWrapper类 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) { } }; } } 5)过滤器类 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() { } } 6)拦截器过滤器配置类 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("/**"); } }