首页 > 其他分享 >Springcloud学习笔记39--拦截器Interceptor详细使用

Springcloud学习笔记39--拦截器Interceptor详细使用

时间:2022-12-29 15:36:03浏览次数:37  
标签:39 拦截器 var1 -- void request import public

原文链接:https://www.cnblogs.com/luckyplj/p/15839125.html

1.拦截器Interceptor定义

拦截器是在面向切面编程中应用的,就是在你的service或者一个方法前调用一个方法,或者在方法后调用一个方法。是基于JAVA的反射机制。

1.1 拦截器(Interceptor)执行顺序

(1)请求到达 DispatcherServlet
(2)DispatcherServlet 发送至 Interceptor ,执行 preHandle
(3)请求达到 Controller
(4)请求结束后,postHandle 执行

 1.2 使用方法

HandlerInterceptorAdapter 这个适配器是由Spring MVC提供的(org.springframework.web.servlet.handler.HandlerInterceptorAdapter)继承此类,可以非常方便的实现自己的拦截器,而且不仅可实现Filter的所有功能,还可以更精确的控制拦截精度(shiro中的拦截器可以说是基于此实现的)

复制代码
public abstract class HandlerInterceptorAdapter implements AsyncHandlerInterceptor {
    public HandlerInterceptorAdapter() {
    }

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //preHandle在业务处理器处理请求之前被调用。预处理,可以进行编码、安全控制等处理;
        return true;
    }

    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
        //在业务处理器处理请求执行完成后,生成视图之前执行。后处理(调用了Service并返回ModelAndView,但未进行页面渲染),有机会修改ModelAndView;
    }

    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
        //在DispatcherServlet完全处理完请求后被调用,可用于清理资源等。返回处理(已经渲染了页面),可以根据ex是否为null判断是否发生了异常,进行日志记录;
    }

    public void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    }
}
复制代码

2.Spring Boot配置方式

2.1 自定义拦截器,需要继承HandlerInterceptorAdapter类

具体案例实现如下:

(1)拦截器AccessLogInterceptor

复制代码
@Slf4j
public class AccessLogInterceptor extends HandlerInterceptorAdapter {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.info("进入到拦截器AccessLogInterceptor中:preHandle() 方法");
        String remoteAddr=getRequestIp(request);
        log.info("接收到来自[{}]请求",remoteAddr);
        return true;
    }

    private String getRequestIp(HttpServletRequest request) {
        String requestIp = request.getHeader("x-forwarded-for");
        return requestIp;

    }
}
复制代码

(2)拦截器AuthorityInterceptor

复制代码
@Slf4j
public class AuthorityInterceptor extends HandlerInterceptorAdapter {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.info("进入到拦截器AuthorityInterceptor中:preHandle() 方法");
        return true;
    }
}
复制代码

2.2 注册拦截器,需要实现WebMvcConfigurer接口

WebMvcConfigurer配置类其实是Spring内部的一种配置方式,采用JavaBean的形式来代替传统的xml配置文件形式进行针对框架个性化定制,可以自定义一些Handler,Interceptor,ViewResolver,MessageConverter。基于java-based方式的spring mvc配置,需要创建一个配置类并实现WebMvcConfigurer 接口;

在Spring Boot 1.5版本都是靠重写WebMvcConfigurerAdapter的方法来添加自定义拦截器,消息转换器等。SpringBoot 2.0 后,该类被标记为@Deprecated(弃用)。官方推荐直接实现WebMvcConfigurer。

WebMvcConfigurer接口代码如下:

复制代码
public interface WebMvcConfigurer {
    void configurePathMatch(PathMatchConfigurer var1);
 
    void configureContentNegotiation(ContentNegotiationConfigurer var1);
 
    void configureAsyncSupport(AsyncSupportConfigurer var1);
 
    void configureDefaultServletHandling(DefaultServletHandlerConfigurer var1);
 
    void addFormatters(FormatterRegistry var1);
 
    void addInterceptors(InterceptorRegistry var1);
 
    void addResourceHandlers(ResourceHandlerRegistry var1);
 
    void addCorsMappings(CorsRegistry var1);
 
    void addViewControllers(ViewControllerRegistry var1);
 
    void configureViewResolvers(ViewResolverRegistry var1);
 
    void addArgumentResolvers(List<HandlerMethodArgumentResolver> var1);
 
    void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> var1);
 
    void configureMessageConverters(List<HttpMessageConverter<?>> var1);
 
    void extendMessageConverters(List<HttpMessageConverter<?>> var1);
 
    void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> var1);
 
    void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> var1);
 
    Validator getValidator();
 
    MessageCodesResolver getMessageCodesResolver();
}
复制代码

需要重写addInterceptors方法,这里是对根目录"/"进行拦截,可以指定拦截url请求目录。

具体案例实现如下:

复制代码
@Configuration
public class MyWebMvcConfigurerAdapter implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        addV1Rule(registry);
    }

    private void addV1Rule(InterceptorRegistry registry) {
        //注册自己的拦截器并设置拦截的请求路径
        registry.addInterceptor(new AccessLogInterceptor()).addPathPatterns("/**");  //拦截所有请求
        registry.addInterceptor(new AuthorityInterceptor()).addPathPatterns("/student/getStudentName");  //拦截student相关请求

    }
}
复制代码

2.3 案例测试结果分析

用户请求的url如下所示:

复制代码
@RestController
@RequestMapping("/student")
@Slf4j
public class StudentController {
    @Autowired
    private IStudentService studentService;

    @PostMapping("/getStudentName")
    public void getStudentName(){
        log.info("studentName:lucky");
    }
}
复制代码

postman请求的url: http://127.0.0.1:7010/student/getStudentName

控制台输出:

复制代码
2022-01-24 13:47:15.599 |  INFO  | http-nio-7010-exec-1 | com.ttbank.flep.core.interceptor.AccessLogInterceptor:18 | [] -进入到拦截器AccessLogInterceptor中:preHandle() 方法
2022-01-24 13:47:15.600 |  INFO  | http-nio-7010-exec-1 | com.ttbank.flep.core.interceptor.AccessLogInterceptor:20 | [] -接收到来自[11112222]请求
2022-01-24 13:47:15.600 |  INFO  | http-nio-7010-exec-1 | com.ttbank.flep.core.interceptor.AuthorityInterceptor:17 | [] -进入到拦截器AuthorityInterceptor中:preHandle() 方法
2022-01-24 13:47:15.605 |  INFO  | http-nio-7010-exec-1 | com.ttbank.flep.core.controller.StudentController:41 | [] -studentName:lucky
复制代码

 3. Springboot拦截器中获取request post请求中@RequestBody注解的Json请求参数

最近有一个需要从拦截器中获取post请求的参数的需求,这里记录一下处理过程中出现的问题。

于是,想到了使用流的方式,调用request.getInputStream()获取流,然后从流中读取参数;但是,经过拦截器后,参数经过@RequestBody注解赋值给controller中的方法的时候,却抛出了一个这样的异常:

org.springframework.http.converter.HttpMessageNotReadableException: Required request body is missing

Spring 中的 request.getInputStream()和 request.getReader()只能被读取一次,而@RequestBody注解底层也是通过流来请求数据,所以需要把拦截器中的数据流保存下来,让 controller 层可以读取的数据。

3.1 自定义一个 RequestWrapper 子类

复制代码
package com.ttbank.flep.core.interceptor;

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;

/**
 * @Author lucky
 * @Date 2022/12/5 14:26
 */
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;
    }
}
复制代码

3.2 拦截器层面

复制代码
package com.ttbank.flep.core.interceptor;

import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * @Author lucky
 * @Date 2022/1/24 9:58
 */
@Slf4j
public class AccessLogInterceptor extends HandlerInterceptorAdapter {


    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.info("进入到拦截器AccessLogInterceptor中:preHandle() 方法");
        String remoteAddr=getRequestIp(request);
        log.info("接收到来自[{}]请求",remoteAddr);
        RequestWrapper requestWrapper = new RequestWrapper(request);
        String body = requestWrapper.getBody();
        System.out.println(body);

        return true;
    }

    private String getRequestIp(HttpServletRequest request) {
        String requestIp = request.getHeader("x-forwarded-for");
        if(StringUtils.isEmpty(requestIp)){
            //获取请求封装的ip
            requestIp=request.getRemoteAddr();
        }
        return requestIp;

    }

}
复制代码

3.3 过滤器Filter,用来把request传递下去

复制代码
package com.ttbank.flep.core.filter;

import com.ttbank.flep.core.interceptor.RequestWrapper;
import org.springframework.stereotype.Component;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

/**
 * @Author lucky
 * @Date 2022/12/5 14:30
 */

@Component
@WebFilter(urlPatterns = "/*")
public class ChannelFilter 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() {

    }
}
复制代码

3.4 在启动类中注册拦截器

复制代码
/**
 * @Author lucky
 * @Date 2021/11/25 16:44
 */

@SpringBootApplication(scanBasePackages="com.ttbank")
@EnableDiscoveryClient
@EnableScheduling
@MapperScan("com.ttbank.flep.core.mapper")
@ServletComponentScan  //注册过滤器注解
public class FileFlepApplication {
    public static void main(String[] args) {
        SpringApplication.run(FileFlepApplication.class,args);
    }

}
复制代码

此时,利用postman测试;

可见,controller中可以正常获取json内容了;

参考文献:

https://blog.csdn.net/zhangpower1993/article/details/89016503

https://blog.csdn.net/kuishao1314aa/article/details/109777304

 https://www.mianshigee.com/note/detail/19996jdn/

https://www.jianshu.com/p/69c6fba08c92

标签:39,拦截器,var1,--,void,request,import,public
From: https://www.cnblogs.com/isme-zjh/p/17012639.html

相关文章

  • 图查询语言 nGQL 简明教程 vol.01 快速入门
    本文旨在让新手快速了解nGQL,掌握方向,之后可以脚踩在地上借助文档写出任何心中的NebulaGraph图查询。视频本教程的视频版在B站这里。准备工作在正式开始nGQL实操......
  • Python if语句Demo
    num=int(input("请输入你的数字:"))if(num<3):print(num+2)elif(num>4andnum<6):print("好吧")else:print("还好")结果: 主要就是逻辑运......
  • PHP 多维数组
    在本教程之前的章节中,我们已经知道数组是一种数/值对的简单列表。不过,有时您希望用一个以上的键存储值。可以用多维数组进行存储。PHP-多维数组多维数组指的是包......
  • Python lambda函数demo
    #匿名函数lambda'''lambda函数使用方法:lambdapamr1,pamr2:functionpamr1/parm2代表是参数function指的是实现逻辑'''#声明函数sum=lambdaarg1,arg2:arg......
  • qt读取txt文件内容
    QFilef("D:\\测试数据\\单波束数据\\灯浮.TGT"); if(!f.open(QIODevice::ReadOnly|QIODevice::Text))//打开指定文件 { QMessageBox::about(NULL,"文件","文件打......
  • linux Makefile 如何将生成的 .o 文件放到指定文件夹
    一、Makefile文件为了方便分析,直接上文件,Makefile文件中的内容如下所示:##Makefile#编译的.o文件和.c文件在同一路径下#$(info"start...")#可执行文件名PROJE......
  • 浅谈编程-----非计算机专业以及非培训班的一些感悟
     工作加起来也快一年了,期间从单纯的GIS开发,转向互联网游戏开发,并且两个公司平台都还算行业里面比较大的厂,今天刚好有时间,就先记录一下我对编程的一些浅薄的看法,也当我编程......
  • PHP for 循环
    PHPfor循环执行代码块指定的次数。PHPfor循环如果您已经提前确定脚本运行的次数,可以使用for循环。语法for(initcounter;testcounter;incrementcounter)......
  • windows 根据父进程pid查找所有子进程id(C++)
    直接上代码:大家直接调用即可#include<iostream>#include<Windows.h>#include<tlhelp32.h>#include<string>#include<vector>usingnamespacestd;vector<DWORD>GetPro......
  • 随笔(十三)『docker安装redis』
    1、进入虚拟机vagrantssh2、切换为rootsuroot3、拉取redis镜像dockerpullredis4、创建实体并启动4.1)mkdir-p/mydata/redis/conf4.2)touch/mydata/redis......