SpringBoot20-基础案例05多个拦截器配置和使用场景

本文源码GitHub:知了一笑https://github.com/cicadasmile/spring-boot-base一、拦截器简介1、拦截器定义拦截器,请求的接口被访问之前,进行拦截然后在之前或之后加入某些操作。拦截是AOP的一种实现策略。 拦截器主要用来按照指定规则拒绝请求。 2、拦截器中应用Token令牌验证请求数据校验用户权限校验放行指定接口二、SpringBoot2.0拦截器用法1、编写两个拦截器自定义类实现HandlerInterceptor接口1)OneInterceptor 拦截器 import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.web.servlet.HandlerInterceptor;import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;/** * 拦截器一 */public class OneInterceptor implements HandlerInterceptor { private static final Logger LOGGER = LoggerFactory.getLogger(OneInterceptor.class.getName()); @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) throws Exception { String url =String.valueOf(request.getRequestURL()) ; LOGGER.info("1、url=="+url); // 放开拦截 return true; } @Override public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception { LOGGER.info("1、postHandle"); } @Override public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception { LOGGER.info("1、afterCompletion"); }}2)TwoInterceptor 拦截器 ...

July 10, 2019 · 1 min · jiezi

vue2-http拦截器-实现每次请求地址都显示loading

实现效果图1、这里使用的是element-ui框架显示的loading,当然使用方法大同小异您可以使用其他框架或者自己写样式。 main.js引入element-ui框架 import ElementUI from 'element-ui'Vue.use(ElementUI)2、main.js中配置拦截器 // 开始拦截Vue.http.interceptors.push((request, next) => { if (request.url.length > 0) { // 判断请求地址 let loadingInstance = ElementUI.Loading.service({ target: document.getElementById('loading'), fullscreen: false, text: '玩命加载中...' }) next((response) => { // 请求完成,关闭loading框 // this.$nextTick(() => { // 以服务的方式调用的 Loading 需要异步关闭 loadingInstance.close() // }) return response }) }})配置参数 target Loading 需要覆盖的 DOM 节点。可传入一个 DOM 对象或字符串;若传入字符串,则会将其作为参数传入 document.querySelector以获取到对应 DOM 节点 object/string — document.bodybody 同 v-loading 指令中的 body 修饰符 boolean — falsefullscreen 同 v-loading 指令中的 fullscreen 修饰符 boolean — truelock 同 v-loading 指令中的 lock 修饰符 boolean — falsetext 显示在加载图标下方的加载文案 string — —spinner 自定义加载图标类名 string — —background 遮罩背景色 string — —customClass Loading 的自定义类名 string — —

June 12, 2019 · 1 min · jiezi

spring boot 集成swagger并且使用拦截器的配置问题

最近同事问我,spring boot集成了swagger,但是在使用拦截器的时候遇到了问题,页面无法访问。经过研究解决了这个问题。配置问题解决集成swagger就不啰嗦了,网上到处都是,直接看配置。同事从网上找到的配置:import com.xxx.xxxx.xxx.xxx.LoginInterceptor;import com.fasterxml.classmate.TypeResolver;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.http.ResponseEntity;import org.springframework.web.context.request.async.DeferredResult;import org.springframework.web.servlet.config.annotation.InterceptorRegistry;import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;import springfox.documentation.builders.ApiInfoBuilder;import springfox.documentation.builders.PathSelectors;import springfox.documentation.builders.RequestHandlerSelectors;import springfox.documentation.schema.WildcardType;import springfox.documentation.service.ApiInfo;import springfox.documentation.service.Contact;import springfox.documentation.spi.DocumentationType;import springfox.documentation.spring.web.plugins.Docket;import springfox.documentation.swagger2.annotations.EnableSwagger2;import java.util.Collections;import static springfox.documentation.schema.AlternateTypeRules.newRule;@Configuration@EnableSwagger2public class Swagger2Config extends WebMvcConfigurationSupport { @Autowired private TypeResolver typeResolver; @Bean public Docket productApi() { return new Docket(DocumentationType.SWAGGER_2) .select() .apis(RequestHandlerSelectors.basePackage(“com.xxx.xxx.controller”)) .paths(PathSelectors.any()) .build() .apiInfo(metaData()) .alternateTypeRules( //自定义规则,如果遇到DeferredResult,则把泛型类转成json newRule(typeResolver.resolve(DeferredResult.class, typeResolver.resolve(ResponseEntity.class, WildcardType.class)), typeResolver.resolve(WildcardType.class))) ; } private ApiInfo metaData() { return new ApiInfoBuilder() .title(“通用服务 APIs”) /.description(""REST API for Online Store"")/ .version(“1.0.0”) /* .license(“Apache License Version 2.0”) .licenseUrl(“https://www.apache.org/licenses/LICENSE-2.0"”)/ .contact(new Contact(“易保科技”, “”, “mail@mail”)) .build(); } @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/”); super.addResourceHandlers(registry); }}这是他从网上找到的拦截器的配置:@Configurationpublic class WebMvcConfig extends WebMvcConfigurationSupport { public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LoginInterceptor()) // addPathPatterns 用于添加拦截规则 , 先把所有路径都加入拦截, 再一个个排除 .addPathPatterns("/") .excludePathPatterns(Collections.singletonList("/swagger-ui.html")); super.addInterceptors(registry); }}现在的测试结果就是,打开http://host:port/path/swagger-ui.html,就是一个空白页面,无法使用,现在要解决的就是这个问题。打开谷歌浏览器的调试控制台,查看network,如图:可以明显看出,页面加载数据的时候,并没有报什么错误,只是加载的资源都被拦截器拦截了,无法加载资源,可想而知,资源都被拦截器拦截了。我分析了一下,加载资源的路径,修改了一下拦截器资源配置: @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(loginInterceptor) // addPathPatterns 用于添加拦截规则 , 先把所有路径都加入拦截, 再一个个排除 .addPathPatterns("/") .excludePathPatterns("/swagger-ui.html") .excludePathPatterns("/swagger-resources/") .excludePathPatterns("/error") .excludePathPatterns("/webjars/"); }另外两个类实际是同一个作用,所以合并两个类:@Configuration@EnableSwagger2public class TestMVCConfig extends WebMvcConfigurationSupport { @Resource private TypeResolver typeResolver; @Resource private LoginInterceptor loginInterceptor; @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/”); } @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(loginInterceptor) // addPathPatterns 用于添加拦截规则 , 先把所有路径都加入拦截, 再一个个排除 .addPathPatterns("/") .excludePathPatterns("/swagger-ui.html") .excludePathPatterns("/swagger-resources/") .excludePathPatterns("/error") .excludePathPatterns("/webjars/**"); } @Bean public Docket productApi() { return new Docket(DocumentationType.SWAGGER_2) .select() .apis(RequestHandlerSelectors.basePackage(“com.xxx.xxx.controller”)) .paths(PathSelectors.any()) .build() .apiInfo(metaData()) .alternateTypeRules( //自定义规则,如果遇到DeferredResult,则把泛型类转成json newRule(typeResolver.resolve(DeferredResult.class, typeResolver.resolve(ResponseEntity.class, WildcardType.class)), typeResolver.resolve(WildcardType.class))) ; } private ApiInfo metaData() { return new ApiInfoBuilder() .title(“通用服务 APIs”) /.description(""REST API for Online Store"")/ .version(“1.0.0”) / .license(“Apache License Version 2.0”) .licenseUrl(“https://www.apache.org/licenses/LICENSE-2.0"”)/ .contact(new Contact(“易保科技”, “”, “mail@mail”)) .build(); }}这样就没有问题了。另外的解决方案网上还有另外一种说法,可以实现WebMvcConfigurer接口,代码如下:@Configuration@EnableWebMvc@EnableSwagger2public class WebMVCConfig implements WebMvcConfigurer{ @Resource private TypeResolver typeResolver; @Resource private LoginInterceptor loginInterceptor; @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/”); } @Override public void addInterceptors(InterceptorRegistry registry) { List<String> excludePathPatterns = new ArrayList<>(); excludePathPatterns.add("/swagger-ui.html"); excludePathPatterns.add("/swagger-resources/"); excludePathPatterns.add("/error"); excludePathPatterns.add("/webjars/"); // addPathPatterns 用于添加拦截规则 , 先把所有路径都加入拦截, 再一个个排除 registry.addInterceptor(loginInterceptor) .addPathPatterns("/") .excludePathPatterns(excludePathPatterns); } @Bean public Docket productApi() { return new Docket(DocumentationType.SWAGGER_2) .select() .apis(RequestHandlerSelectors.basePackage(“com.xx.sss.controller”)) .paths(PathSelectors.any()) .build() .apiInfo(metaData()) .alternateTypeRules( //自定义规则,如果遇到DeferredResult,则把泛型类转成json newRule(typeResolver.resolve(DeferredResult.class, typeResolver.resolve(ResponseEntity.class, WildcardType.class)), typeResolver.resolve(WildcardType.class))) ; } private ApiInfo metaData() { return new ApiInfoBuilder() .title(“通用服务 APIs”) /.description(""REST API for Online Store"")/ .version(“1.0.0”) / .license(“Apache License Version 2.0”) .licenseUrl(“https://www.apache.org/licenses/LICENSE-2.0"”)*/ .contact(new Contact(“易保科技”, “”, “mail@mail”)) .build(); }}但是这种配置想要生效,必须加@EnableWebMvc注解,不然不起作用。为什么?官方源码注释:/** * Defines callback methods to customize the Java-based configuration for * Spring MVC enabled via {@code @EnableWebMvc}. * * <p>{@code @EnableWebMvc}-annotated configuration classes may implement * this interface to be called back and given a chance to customize the * default configuration. *通过@enableWebMVC启用Spring MVC,自定义基于Java config 定义回调方法。@EnableWebMVC带注释的配置类可以实现这个接口自定义默认配置。当然如果你觉得自己的配置没问题,但是仍然不起作用,这时候改怎么办?请按照一下步骤debug:1、找到InterceptorRegistration类;2、找到addInterceptor方法和excludePathPatterns方法,打上断点;3、debug模式启动项目;如果没有进入断点,那就说明你的配置根本没有起到作用,看看注解是否没写。如果进入了断点,就要看看断点处传进来的参数是否是你配置的参数,不是那就是有问题,这时候再根据参数查找问题。这样基本就能解决问题了。总结网上很多东西都是抄来抄去,也不知道有没有验证,让很多人摸不着头脑。 ...

April 18, 2019 · 2 min · jiezi

利用责任链模式设计一个拦截器

前言近期在做 Cicada 的拦截器功能,正好用到了责任链模式。这个设计模式在日常使用中频率还是挺高的,借此机会来分析分析。责任链模式先来看看什么是责任链模式。引用一段维基百科对其的解释:责任链模式在面向对象程式设计里是一种软件设计模式,它包含了一些命令对象和一系列的处理对象。每一个处理对象决定它能处理哪些命令对象,它也知道如何将它不能处理的命令对象传递给该链中的下一个处理对象。该模式还描述了往该处理链的末尾添加新的处理对象的方法。光看这段描述可能大家会觉得懵,简单来说就是该设计模式用于对某个对象或者请求进行一系列的处理,这些处理逻辑正好组成一个链条。下面来简单演示使用与不使用责任链模式有什么区别和优势。责任链模式的应用传统实现假设这样的场景:传入了一段内容,需要对这段文本进行加工;比如过滤敏感词、错别字修改、最后署上版权等操作。常见的写法如下:public class Main { public static void main(String[] args) { String msg = “内容内容内容” ; String result = Process.sensitiveWord() .typo() .copyright(); }}这样看似没啥问题也能解决需求,但如果我还需要为为内容加上一个统一的标题呢?在现有的方式下就不得不新增处理方法,并且是在这个客户端(Process)的基础上进行新增。显然这样的扩展性不好。责任链模式实现这时候就到了责任链模式发挥作用了。该需求非常的符合对某一个对象、请求进行一系列处理的特征。于是我们将代码修改:这时 Process 就是一个接口了,用于定义真正的处理函数。public interface Process { /** * 执行处理 * @param msg / void doProcess(String msg) ;}同时之前对内容的各种处理只需要实现该接口即可:public class SensitiveWordProcess implements Process { @Override public void doProcess(String msg) { System.out.println(msg + “敏感词处理”); }}public class CopyrightProcess implements Process { @Override public void doProcess(String msg) { System.out.println(msg + “版权处理”); }}public class CopyrightProcess implements Process { @Override public void doProcess(String msg) { System.out.println(msg + “版权处理”); }}然后只需要给客户端提供一个执行入口以及添加责任链的入口即可:public class MsgProcessChain { private List<Process> chains = new ArrayList<>() ; /* * 添加责任链 * @param process * @return / public MsgProcessChain addChain(Process process){ chains.add(process) ; return this ; } /* * 执行处理 * @param msg */ public void process(String msg){ for (Process chain : chains) { chain.doProcess(msg); } }}这样使用起来就非常简单:public class Main { public static void main(String[] args) { String msg = “内容内容内容==” ; MsgProcessChain chain = new MsgProcessChain() .addChain(new SensitiveWordProcess()) .addChain(new TypoProcess()) .addChain(new CopyrightProcess()) ; chain.process(msg) ; }}当我需要再增加一个处理逻辑时只需要添加一个处理单元即可(addChain(Process process)),并对客户端 chain.process(msg) 是无感知的,不需要做任何的改动。可能大家没有直接写过责任链模式的相关代码,但不经意间使用到的却不少。比如 Netty 中的 pipeline 就是一个典型的责任链模式,它可以让一个请求在整个管道中进行流转。通过官方图就可以非常清楚的看出是一个责任链模式:用责任链模式设计一个拦截器对于拦截器来说使用责任链模式再好不过了。下面来看看在 Cicada 中的实现:首先是定义了和上文 Process 接口类似的 CicadaInterceptor 抽象类:public abstract class CicadaInterceptor { public boolean before(CicadaContext context,Param param) throws Exception{ return true; } public void after(CicadaContext context,Param param) throws Exception{}}同时定义了一个 InterceptProcess 的客户端:其中的 loadInterceptors() 会将所有的拦截器加入到责任链中。再提供了两个函数分别对应了拦截前和拦截后的入口:实际应用现在来看看具体是怎么使用的吧。在请求的 handle 中首先进行加载(loadInterceptors(AppConfig appConfig)),也就是初始化责任链。接下来则是客户端的入口;调用拦截前后的入口方法即可。由于是拦截器,那么在 before 函数中是可以对请求进行拦截的。只要返回 false 就不会继续向后处理。所以这里做了一个返回值的判断。同时对于使用者来说只需要创建拦截器类继承 CicadaInterceptor 类即可。这里做了一个演示,分别有两个拦截器:记录一个业务 handle 的执行时间。在 after 里打印了请求参数。同时可在第一个拦截器中返回 false 让请求被拦截。先来做前两个试验:这样当我请求其中一个接口时会将刚才的日志打印出来:接下来我让打印执行时间的拦截器中拦截请求,同时输入向前端输入一段文本:请求接口可以看到如下内容:同时后面的请求参数也没有打印出来,说明请求确实被拦截下来。同时我也可以调整拦截顺序,只需要在 @Interceptor(order = 1) 注解中定义这个 order 属性即可(默认值是 0,越小越先执行)。之前是打印请求参数的拦截器先执行,这次我手动将它的 order 调整为 2,而打印时间的 order 为 1 。再次请求接口观察后台日志:发现打印执行时间的拦截器先执行。那这个执行执行顺序如何实现自定义配置的呢?其实也比较简单,有以下几步:在加载拦截器时将注解里的 order 保存起来。设置拦截器到责任链中时通过反射将 order 的值保存到各个拦截器中。最终通过排序重新排列这个责任链的顺序。贴一些核心代码。扫描拦截器时保存 order 值:保存 order 值到拦截器中:重新对责任链排序:总结整个责任链模式已经讲完,希望对这个设计模式还不了解的朋友带来些帮助。上文中的源码如下:https://github.com/TogetherOS/cicada:一个高性能、轻量 HTTP 框架https://git.io/fxKid欢迎关注公众号一起交流: ...

October 22, 2018 · 2 min · jiezi