SpringBoot项目的创建和初始化在这里就不在重复
一.实现请求拦截器处理数据
自定义拦截器
在SpringMVC中的 Interceptor拦截器才用实现 HandlerInterceptor的方式,实现其中的三个方法。
preHandle(): 该方法在请求处理之前执行,SpringMVC 中的Interceptor 是链式的调用的,在一个应用中或者说是在一个请求中可以同时存在多个Interceptor 。每个Interceptor 的调用会依据它的声明顺序依次执行,而且最先执行的都是Interceptor 中的preHandle 方法。
postHandle(): 该方法在请求处理之后执行,但是他会在 DispatcherServlet 进行视图返回渲染之前被调用,所以我们可以在这个方法中对Controller处理之后的 ModeAndView 对象进行操作.需要注意的是,如果 Controller 执行过程中出现了异常,那么并不会执行该方法,而是执行 afterCompletion方法。
afterCompletion(): 在postHandle执行之后执行,发生异常也会执行,通常用于释放系统资源。
@Component
public class RecordIntercepter implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//获取请求参数,进行验证,记录等操作
return false;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
}
}
注册拦截器
创建一个配置类继承WebMvcConfigurerAdapter类,并重写addInterceptors方法,将需要添加的自定义拦截器对象添加进去即可
addPathPatterns():添加需要拦截的路径
excludePathPatterns():添加不需要拦截的路径
@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {
@Override
protected void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(recordIntercepter()).addPathPatterns("/api/**").excludePathPatterns("/login");
}
}
@Bean
public RecordIntercepter recordIntercepter(){
return new RecordIntercepter();
}
经过上面的配置后已经实现了在拦截器中获取到参数记录日志的效果,但是会发现在 controller中的@RequestBody 注解获取不到参数了,Spring 中的 request.getInputStream()和 request.getReader()只能被读取一次,而@RequestBody注解底层也是通过流来请求数据,所以需要把拦截器中的数据流保存下来,让 controller 层可以读取的数据
自定义一个 RequestWrapper 子类
继承 HttpServletRequestWrapper类,封装HttpServletRequest请求
public class RequestWrapper extends HttpServletRequestWrapper {
private final String body;
public RequestWrapper(HttpServletRequest request) {
super(request);
StringBuilder stringBuilder = new StringBuilder();
BufferedReader bufferedReader = null;
InputStream inputStream = null;
try {
inputStream = request.getInputStream();
if (inputStream != null) {
bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
char[] charBuffer = new char[128];
int bytesRead = -1;
while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
stringBuilder.append(charBuffer, 0, bytesRead);
}
} else {
stringBuilder.append("");
}
} catch (IOException ex) {
} finally {
if (inputStream != null) {
try {
inputStream.close();
}
catch (IOException e) {
e.printStackTrace();
}
}
if (bufferedReader != null) {
try {
bufferedReader.close();
}
catch (IOException e) {
e.printStackTrace();
}
}
}
body = stringBuilder.toString();
}
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());
ServletInputStream servletInputStream = new ServletInputStream() {
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
@Override
public int read() throws IOException {
return byteArrayInputStream.read();
}
};
return servletInputStream;
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(this.getInputStream()));
}
public String getBody() {
return this.body;
}
}
自定义过滤器
通过自定义过滤器,将流继续保存下去
@Component
@WebFilter(urlPatterns = "/**")
public class RecordChannelFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
ServletRequest requestWrapper = null;
if(servletRequest instanceof HttpServletRequest) {
requestWrapper = new RequestWrapper((HttpServletRequest) servletRequest);
}
if(requestWrapper == null) {
//防止流读取一次就没有了,将流传递下去
filterChain.doFilter(servletRequest, servletResponse);
} else {
filterChain.doFilter(requestWrapper, servletResponse);
}
}
@Override
public void destroy() {
}
}
这样在 controller层使用@Requestbody 注解就可以正常获取到流中的数据
二.自定义返回值拦截器
需求:需要对 controller执行完成后返回值中的数据进行二次封装或者进行记录处理等操作.
因为SpringBoot默认的ResponseBody的处理程序就是HandlerMethodReturnValueHandler,所以我们自己写的HandlerMethodReturnValueHandler通常无法生效,
非要使用HandlerMethodReturnValueHandler,那么只能替换掉默认的,
如果只是想对Controller的所有返回值进行封装,产生上面的效果,使用ResponseBodyAdvice会更加简单一些
@ControllerAdvice
public class InterceptResponse implements ResponseBodyAdvice<Object> {
@Override
public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
return true;
}
@Nullable
@Override
public Object beforeBodyWrite(@Nullable Object body, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
ServletServerHttpResponse responseTemp = (ServletServerHttpResponse) serverHttpResponse;
HttpServletResponse resp = responseTemp.getServletResponse();
ServletServerHttpRequest sshr = (ServletServerHttpRequest) serverHttpRequest;
HttpServletRequest req = sshr.getServletRequest();
//此处的 Result 对象是我自定义的返回值类型,具体根据自己需求修改即可
if(body instanceof Result){
Result result = (Result) body;
if(result!=null) {
String s = result.getResult()!=null?result.getResult().toString():null;
//记录日志等操作
}
//这里可以对返回值进行修改二次封装等操作
}
return body;
}
}