拦截器使用总结

本文总结了Spring拦截器的使用,包括拦截器中获取请求体的问题、配置拦截地址、异常处理以及如何配合过滤器填充请求体。强调了在拦截器中处理异常的重要性,并提出使用切面可能更为简便。最后分享了过滤器的配置方式,提供了避免拦截错误处理的建议。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

拦截器使用总结

过滤器实现

1.拦截器中 获取body里面的内容后,controller获取body的值将会为空 会抛异常

@Order(1001)
//集成HandlerInterceptorAdapter实现
public class TokenInterceptors extends HandlerInterceptorAdapter {

    private Logger logger = LoggerFactory.getLogger(TokenInterceptors.class);

	//过滤器参数可以由构造器 外部传进来
    public String screct;

    public TokenInterceptors(String screct) {
        this.screct = screct;
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        String body = new String();
        //此处获取body使用Filter修饰过的 直接使用request.getInputStream()获取数据的话,controller将获取不到数据
        if(request instanceof BodyReaderHttpServletRequestWrapper){
            body = ((BodyReaderHttpServletRequestWrapper) request).getBodyStr();
        }

        String url = request.getRequestURI();
        String method = request.getMethod();
        String tokenReq = request.getParameter("token");

        BaseResult baseResult = new BaseResult();
        baseResult.setCode("-1");


        if (!method.equals("POST")){
            logger.error("request method is not post");
            baseResult.setMsg("request method is not post");
            returnJson(response, JsonUtil.toJSONNoFeatures(baseResult));
            return false;
        }
        String token = TokenUtil.getToken(url, body, screct);
        if (token.equals(tokenReq)) {
            return super.preHandle(request, response, handler);
        }
        else {
            logger.error("request token is error ");
            baseResult.setMsg("request token is error");
            returnJson(response, JsonUtil.toJSONNoFeatures(baseResult));
            return false;
        }

    }
    /**
     * 获取POST请求中Body参数----------------这个方法使用request.getInputStream了 controller将获取不到body
     * @param request
     * @return 字符串
     */
    public String getParm(HttpServletRequest request) {
        BufferedReader br = null;
        try {
            br = new BufferedReader(new InputStreamReader(request.getInputStream(), "UTF-8"));
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        String line = null;
        StringBuilder sb = new StringBuilder();
        try {
            while ((line = br.readLine()) != null) {
                sb.append(line);
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return sb.toString();
    }

    private void returnJson(HttpServletResponse response, String json) throws Exception {
        PrintWriter writer = null;
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/html; charset=utf-8");
        try {
            writer = response.getWriter();
            writer.print(json);

        } catch (IOException e) {
            logger.error("response error", e);
        } finally {
            if (writer != null)
                writer.close();
        }
    }


    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
                                Object handler, Exception ex) throws Exception {
    }


    @Override
    public void afterConcurrentHandlingStarted(HttpServletRequest request,
                                               HttpServletResponse response, Object handler) throws Exception {

    }
}

拦截器配置

2.继承WebMvcConfigurationSupport(springboot2.x后用此类) WebMvcConfigurerAdapter

1.配置拦截地址和不拦截地址

3.addResourceHandlers增加获取本地文件 或者不拦截的url handle 比如swagger,当然地址可以在拦截器的不拦截地址里面配置 效果一样。

@Configuration
@Order(1001)
public class TokenConfig extends WebMvcConfigurerAdapter {
    @Value("${sddi.custom.api.screct}")
    public String screct;
    /**
     *拦截器排除的URL
     */
    @Value("${token.interceptor.excludePathPatterns:}")
    private String excludePathPatterns;

    /**
     *拦截URL
     */
    @Value("/**")
    private String pathPatterns;

    // 这个方法用来注册拦截器,我们自己写好的拦截器需要通过这里添加注册才能生效
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        InterceptorRegistration interceptorRegistration = registry.addInterceptor(
                new TokenInterceptors(screct));

        String[] paths = this.pathPatterns.split(",");

        for(int i = 0; i < paths.length; ++i) {
            interceptorRegistration.addPathPatterns(new String[]{paths[i]});
        }
        //不拦截的地址
        if (!StringUtils.isEmpty(this.excludePathPatterns)) {
            String[] excludePaths = this.excludePathPatterns.split(",");

            for(int i = 0; i < excludePaths.length; ++i) {
                interceptorRegistration.excludePathPatterns(new String[]{excludePaths[i]});
            }
        }

        super.addInterceptors(registry);
    }


    // 这个方法是用来配置静态资源的,比如html,js,css,等等
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("swagger-ui.html")
                .addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**")
                .addResourceLocations("classpath:/META-INF/resources/webjars/");
    }
}

4.注意点,当拦截器通过后,如果调用controller方法出现异常,这个时候如果没有 异常处理ControllerAdvice,会进入到SpringBoot默认全局异常,也就是我们的/error请求,最尴尬的是,这个请求如果没有在拦截器中配置/**/error的不过滤,这个请求也会进入到拦截器,这个时候 就会进入拦截器拦截逻辑,返回的就是是拦截器错误给到前端。

@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class BasicErrorController extends AbstractErrorController {

	private final ErrorProperties errorProperties;
..........

解决方法就是写一个异常拦截器@RestControllerAdvice的实现。

@RestControllerAdvice(basePackages = "com.hikvision.sddicustom.business.controller")
public class ExceptionHandlerAdvice {
    private static final HikGaLogger LOGGER = HikGaLoggerFactory.getLogger(ExceptionHandlerAdvice.class);


    /**
     * 参数校验异常 validate框架
     * 对象参数接收请求体: MethodArgumentNotValidException
     * 请求参数绑定到对象参数上: BindException
     * 普通参数: ConstraintViolationException
     * 必填参数没传: ServletRequestBindingException
     * 必填请求参数缺失:MissingServletRequestParameterException
     * 路径参数缺失:MissingPathVariableException
     * @param ex
     * @return
     */
    @ExceptionHandler(value = {ValidationException.class, ConstraintViolationException.class,
            MethodArgumentNotValidException.class, ServletRequestBindingException.class, BindException.class,
            MissingPathVariableException.class, MissingServletRequestParameterException.class})
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public BaseResult handleBadRequestException(Exception ex) {

        String errorCode = ErrorCode.PARAMETER_VALIDATE_EXCEPTION.getCode();
        String msg = "";
        //validate参数校验异常获取具体描述信息
        if (ex instanceof ValidationException) {
            ValidationException validationException = (ValidationException)ex;
            if (!StringUtils.isEmpty(validationException.getMessage())) {
                String validteMsg = validationException.getMessage();
                if(!StringUtils.isEmpty(validteMsg) && validteMsg.indexOf(":") > -1){
                    String[] msgArr = validteMsg.split(":");
                    if(!StringUtils.isEmpty(msgArr[1])){
                        msg = msgArr[1];
                    }
                }
            }
        }
        LOGGER.errorWithErrorCode(errorCode,msg,ex);
        return BaseResult.error(errorCode,msg);
    }




    /**
     * 未知异常处理
     * @param ex
     * @return
     */
    @ExceptionHandler(value = {Exception.class})
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public BaseResult handleRunTimeException(Exception ex) {
        LOGGER.errorWithErrorCode(ErrorCode.UNKNOWN_EXCEPTION.getCode(),ErrorCode.UNKNOWN_EXCEPTION.getChinaMessage(),ex);
        return BaseResult.fail(ErrorCode.UNKNOWN_EXCEPTION.getCode(),ErrorCode.UNKNOWN_EXCEPTION.getChinaMessage());
    }
}

下面给出获取body的方法 —需要使用过滤器fillter ----这里不是为了介绍Filter的使用,这里的filter是为了配合拦截器使用。

1.封装HttpServletRequestWrapper获取body

public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper {


    private final byte[] body;
    private String bodyStr;

    public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        String bodyString = getBodyString(request);
        body = bodyString.getBytes(Charset.forName("UTF-8"));
        bodyStr=bodyString;
    }

    public String getBodyStr() {
        return bodyStr;
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body);

        return new ServletInputStream() {
            @Override
            public int read() throws IOException {
                return byteArrayInputStream.read();
            }

            @Override
            public boolean isFinished() {
                return false;
            }

            @Override
            public boolean isReady() {
                return false;
            }

            @Override
            public void setReadListener(ReadListener readListener) {
            }
        };
    }


    public  String getBodyString(HttpServletRequest request) throws IOException {
        StringBuilder sb = new StringBuilder();
        InputStream inputStream = null;
        BufferedReader reader = null;
        try {
            inputStream = request.getInputStream();
            reader = new BufferedReader(
                    new InputStreamReader(inputStream, Charset.forName("UTF-8")));

            char[] bodyCharBuffer = new char[1024];
            int len = 0;
            while ((len = reader.read(bodyCharBuffer)) != -1) {
                sb.append(new String(bodyCharBuffer, 0, len));
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return sb.toString();
    }
}

2.使用Filter进行 request转换,加上WebFilter注解 还需要使用Component 或者@ServletComponentScan来让Filter起作用(配置Filter的方式很多 这种偷懒的方式)

@WebFilter(filterName = "httpServletRequestWrapperFilter", urlPatterns = {"/*"})
@Component
public class HttpServletRequestWrapperFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {

        ServletRequest requestWrapper = null;

        if (request instanceof HttpServletRequest) {
            HttpServletRequest httpRequest = (HttpServletRequest) request;
            //遇到post方法才对request进行包装
            String methodType = httpRequest.getMethod();
            if ("POST".equals(methodType)) {
                requestWrapper = new BodyReaderHttpServletRequestWrapper(
                        (HttpServletRequest) request);
            }
        }
        if (null == requestWrapper) {
            chain.doFilter(request, response);
        } else {
            chain.doFilter(requestWrapper, response);
        }
    }

    @Override
    public void destroy() {

    }
}

过滤器的添加方式 这边 顺带提一下,以后再补充吧

第一种在Application中加一次 继承ServletContextInitializer

public class RpidWebApplicatin implements ServletContextInitializer {

    public static void main(String[] args) {
        SpringApplication.run(RpidWebApplicatin.class, args);
    }

    @Override
    public void onStartup(ServletContext sc) throws ServletException {
        sc.addListener(new SingleSignOutHttpSessionListener());
        FilterRegistration.Dynamic cASLogoutFilter = sc.addFilter("CASLogoutFilter", SingleSignOutFilter.class);
        cASLogoutFilter.addMappingForUrlPatterns(null, false, "/*");
        FilterRegistration.Dynamic cASAuthenticationFilter = sc.addFilter("CAS Authentication Filter",
                HikAuthenticationFilter.class);

        cASAuthenticationFilter.addMappingForUrlPatterns(null, false, "/*");
        FilterRegistration.Dynamic cas20Registration = sc.addFilter("CAS Validation Filter",
                HikCas20ProxyReceivingTicketValidationFilter.class);
        cas20Registration.setInitParameter("encoding", "UTF-8");
        cas20Registration.addMappingForUrlPatterns(null, false, "/*");
    }
}

第二种 使用FilterRegistrationBean

@Bean
public FilterRegistrationBean myFilter(){
    FilterRegistrationBean registrationBean = new FilterRegistrationBean();
    registrationBean.setFilter(new MyFilter());
    registrationBean.setUrlPatterns(Arrays.asList("/hello","/myServlet"));
    return registrationBean;
}

实际看源码会发现

ServletContextInitializer->RegistrationBean->FilterRegistrationBean

ServletContextInitializer是最基本的类 FilterRegistrationBean只是对过滤器的继承实现

实际servlet 和 listener filter都是有RegistrationBean的

最终看自己的喜欢去使用过滤器的使用了

其实自己搞了一个下午 最终发现上面的功能使用 切面去实现 是最方便的 使用拦截器有点复杂 不过多学习了一点知识 也是很好的 希望上面的坑大家遇到了 能对大家有好处。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

星仔说

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值