过滤器实现URL拦截,跳转URL

过滤器实现URL拦截,跳转URL

一,背景

业务存在pc和pad两个客户端,pc已经上线。并且pc和pad大部分的接口都可以复用,为了避免重复的代码写2份(即相同的controller),所以目标是pc和pad共用controller,至于pad新的接口,则pad专属。

二,实现

为了实现pc和pad接口公用,pad会在url上传入特定的前缀,比如/pad。

使用过滤器,在DispatcherServlet#doDispatch方法执行前,将url进行替换。
AbstractHandlerMethodMapping的属性mappingRegistry,是保存了URL相关信息,以及handlerMethod的相关信息的,并且在Spring初始化的时候,在对象RequestMappingHandlerMapping里会被赋值。
于是替换思路就有了:
1.在过滤器中拦截url
2.如果URL不包含目标前缀,则FilterChain#doFilter正常放过
3.否则在Spring容器中取出, 并且通过该对象的getHandlerMethods(),拿到保存的URL相关的handlerMethodsMap
4.通过该map能找到信息的,就意味着是pad端独有的接口,通过FilterChain#doFilter正常放过
5.找不到信息的,就将URL中目标前缀替换为空后执行FilterChain#doFilter进行放过。

三,实现代码

拦截器:

@Slf4j
@Component
public class MyOncePerRequestFilter extends OncePerRequestFilter {


    private static final String PREFIX = "/pad";

    // 从spring里面拿  RequestMappingHandlerMapping

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {


        String originalRequestURI = request.getRequestURI();
        if (StrUtil.isNotEmpty(originalRequestURI)) {
            // http://localhost:8002/v1/te/turn/get/query?item=95270244541
            // 输出的结果:===== 获取的 requestURI:/v1/te/turn/get/query
            System.out.println("===== 获取的 requestURI:" + originalRequestURI);

            if (originalRequestURI.contains(PREFIX)) {

                // {GET /v1/te/turn/get/query}
                String httpMethodType = request.getMethod();
                String queryURI = "{" + httpMethodType + " " + originalRequestURI + "}";

                // 在DispatcherServlet存放urls里面找,找不到则去掉prefix
                if (!MyUrlMappingContainer.urlMappingContainer.contains(queryURI)) {

                    String targetURI = originalRequestURI.replace(PREFIX, "");
                    StringBuffer requestURL = request.getRequestURL();
                    String tempURLStr = requestURL.toString();
                    StringBuffer stringBuilder = new StringBuffer(tempURLStr.replace(PREFIX, ""));

                    // 内部类的写法
                    final HttpServletRequestWrapper requestWrapper = new HttpServletRequestWrapper(request) {

                        @Override
                        public StringBuffer getRequestURL() {
                            return stringBuilder;
                        }

                        @Override
                        public String getRequestURI() {
                            return targetURI;
                        }

                        @Override
                        public String getServletPath() {
                            return targetURI;
                        }
                    };

                    filterChain.doFilter(requestWrapper, response);
                    return;
                }
            }
        }

        filterChain.doFilter(request, response);

    }

}

封装的获取Spring初始的URL信息

@Order
@Component
public class MyUrlMappingContainer {

    static final List<String> urlMappingContainer = new ArrayList<>();

    @Autowired
    public void initContainer(ApplicationHelper applicationHelper) {

        RequestMappingHandlerMapping mapping = applicationHelper.getBeanByClass(RequestMappingHandlerMapping.class);
        // Key 是个对象 requestMappingInfo
        // RequestMappingInfo requestMappingInfo = new RequestMappingInfo();
        Map handlerMethodsMap = mapping.getHandlerMethods();

        for (Object obj : handlerMethodsMap.keySet()) {

            urlMappingContainer.add(obj.toString());
        }

        System.out.println(JSON.toJSONString(urlMappingContainer));
    }
}

ps:
如何找到RequestMappingHandlerMapping 类的
通过断点调试

在controller打上断点,比如:
在这里插入图片描述
然后找调用堆栈
就能找到DispatcherServlet这个分发器,以及在这个DispatcherServlet之前执行的filter(这个顺序就印证了过滤器是在请求分发前执行的)

找到这个方法,断点进入 org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#invokeHandlerMethod
在这里插入图片描述

往上找
找到这个方法
org.springframework.web.servlet.DispatcherServlet#getHandler


可以看出 mappedHandler = getHandler(processedRequest);这个方法,通过请求,返回了匹配的controller方法。也就是说这个方法就是关键。
后续的就不写了。哈哈哈哈哈

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值