Web处理,会经过多个路段:过滤器(全局) -> 路由拦截器 -> (处理器) -> 拦截器。可通过 《请求处理过程示意图》 了解。
1、过滤器,全局请求的管控(Filter)[环绕式]@FunctionalInterfacepublic interface Filter {void doFilter(Context ctx, FilterChain chain) throws Throwable;}过滤器,一般用于:
全局的 Web 请求异常处理(包括 '静态' 与 '动态' 等...请求)全局的性能记时全局的响应状态调整全局的上下文日志记录全局的链路跟踪等...也可用于:
局部分的注解附加本地网关过滤@Slf4j@Component(index = 0) //index 为顺序位(不加,则默认为0)public class AppFilter implements Filter {@Overridepublic void doFilter(Context ctx, FilterChain chain) throws Throwable {//1.开始计时(用于计算响应时长)long start = System.currentTimeMillis();try {//2.记录请求数据日志log.info("Request data: {}", getRequestData(ctx)); //getRequestData 需要自己写,把 ctx 里的请求数据转为 string//3.执行处理chain.doFilter(ctx);//4.记录响应数据日志log.info("Response data: {}", getResponseData(ctx.result)); //getResponseData 需要自己写,把 ctx 里的请求数据转为 string} catch (StatusException e) {//5.状态异常,一般是 4xx 错误ctx.status(e.getCode());} catch (Throwable e) {//6.其它异常捕捉与控制(并标为500错误)ctx.status(500);log.error("{}", e);}//5.获得接口响应时长long times = System.currentTimeMillis() - start;System.out.println("用时:"+ times);}}再例如,如果你想把 "/" 转为静态文件 "/index.html" 上(使用 pathNew):
@Component(index = 0) //index 为顺序位(不加,则默认为0)public class AppFilter implements Filter {@Overridepublic void doFilter(Context ctx, FilterChain chain) throws Throwable {if("/".equals(ctx.pathNew())){ //ContextPathFilter 就是类似原理实现的ctx.pathNew("/index.html");}chain.doFilter(ctx);}} ////也可以在控制器做类似处理(不过,会多一轮处理)//@Controllerpublic class HomeController {@Mapping("/")public void home(Context ctx) {//内部跳转到 /index.htmctx.forward("/index.htm");}}2、路由拦截器,全局路由的拦截(RouterInterceptor)[环绕式]@FunctionalInterfacepublic interface RouterInterceptor {//路径匹配模式default PathRule pathPatterns(){return null; //null 表示全部}//执行拦截void doIntercept(Context ctx, @Nullable Handler mainHandler, RouterInterceptorChain chain) throws Throwable;/** * 提交参数(MethodWrap::invokeByAspect 执行前调用) */default void postArguments(Context ctx, ParamWrap[] args, Object[] vals) throws Throwable {}//提交结果(action / render 执行前调用)default Object postResult(Context ctx, @Nullable Object result) throws Throwable {return result;}}RouterInterceptor 和 Filter 差不多。区别有二:1. 只对路由器范围内的处理进行拦截,对静态资源无效;2. 增加了Mvc 参数与结果值的提交确认(即可以修改参数与返回结果)。
当不存在此路由时,mainHandler 为 null通过对 mainHandler 类型检测,可以判断是不是 Action 类型例1:
@Componentpublic class GlobalTransInterceptor implements RouterInterceptor {@Injectprivate TransService transService;/** * 拦截处理(包围式拦截) //和过滤器的 doFilter 类似,且只对路由器范围内的处理有效 */@Overridepublic void doIntercept(Context ctx, Handler mainHandler, RouterInterceptorChain chain) throws Throwable {//提示:如果有需要,可以获取 Action。进而获取控制器和函数//Action action = (mainHandler instanceof Action ? (Action) mainHandler : null); ////提示:这里和 doFilter 差不多...chain.doIntercept(ctx, mainHandler);} /** * 提交结果( render 执行前调用)//不要做太复杂的事情 */@Overridepublic Object postResult(Context ctx, Object result) throws Throwable {//提示:此处只适合做结果类型转换if (result != null && !(result instanceof Throwable) && ctx.action() != null) {result = transService.transOneLoop(result, true);}return result;}}例2:(设定路径匹配模式)
@Componentpublic class AdminAuthInterceptorImpl implements RouterInterceptor {@Injectprivate AuthService authService;/*** 路径限制规则*/@Overridepublic PathRule pathPatterns() {return new PathRule().include("/admin/**").exclude("/admin/login");}/** * 拦截处理(包围式拦截) //和过滤器的 doFilter 类似,且只对路由器范围内的处理有效 */@Overridepublic void doIntercept(Context ctx, Handler mainHandler, RouterInterceptorChain chain) throws Throwable {if(authService.isLogined(ctx)){chain.doIntercept(ctx, mainHandler);}else{ctx.render(Result.failure(401,"账号未登录"));}}}3、拦截器,对Method拦截(Interceptor)[环绕式]只有被动态代理的Bean,才能对Method进行拦截。一般用于切面开发,用注解做为切点配合起来用。比如缓存控制注解@Cache、事务控制注解@Tran等。