Web处理，会经过多个路段：过滤器(全局) -> 路由拦截器 -> (处理器) -> 拦截器。可通过 [《请求处理过程示意图》](/article/242) 了解。


### 1、过滤器，全局请求的管控（Filter）[环绕式]


```java
@FunctionalInterface
public interface Filter {
    void doFilter(Context ctx, FilterChain chain) throws Throwable;
}
```

过滤器，一般用于：

* 全局的 Web 请求异常处理（包括 '静态' 与 '动态' 等...请求）
* 全局的性能记时
* 全局的响应状态调整
* 全局的上下文日志记录
* 全局的链路跟踪等...

也可用于：

* 局部分的注解附加
* 本地网关过滤


```java
@Slf4j
@Component(index = 0) //index 为顺序位（不加，则默认为0）
public class AppFilter implements Filter {
    @Override
    public 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）：
```java
@Component(index = 0) //index 为顺序位（不加，则默认为0）
public class AppFilter implements Filter {
    @Override
    public void doFilter(Context ctx, FilterChain chain) throws Throwable {
        if("/".equals(ctx.pathNew())){ //ContextPathFilter 就是类似原理实现的
            ctx.pathNew("/index.html");
        }

        chain.doFilter(ctx);
    }
}  

//
//也可以在控制器做类似处理（不过，会多一轮处理）
//
@Controller
public class HomeController {
    @Mapping("/")
    public void home(Context ctx) {
        //内部跳转到 /index.htm
        ctx.forward("/index.htm");
    }
}
```

### 2、路由拦截器，全局路由的拦截（RouterInterceptor）[环绕式]


```java
@FunctionalInterface
public 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：

```java
@Component
public class GlobalTransInterceptor implements RouterInterceptor {
    @Inject
    private TransService transService;

    /**
     * 拦截处理（包围式拦截） //和过滤器的 doFilter 类似，且只对路由器范围内的处理有效
     */
    @Override
    public 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 执行前调用）//不要做太复杂的事情
     */
    @Override
    public 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：（设定路径匹配模式）

```java
@Component
public class AdminAuthInterceptorImpl implements RouterInterceptor {
    @Inject
    private AuthService authService;
    
    /**
    * 路径限制规则
    */
    @Override
    public PathRule pathPatterns() {
        return new PathRule().include("/admin/**").exclude("/admin/login");
    }

    /**
     * 拦截处理（包围式拦截） //和过滤器的 doFilter 类似，且只对路由器范围内的处理有效
     */
    @Override
    public 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、事务控制注解@Transaction。

更多参考：[《切面与环绕拦截（AOP）》](/article/35)、[《@Around 使用说明（AOP）》](/article/617)
