一、起源

在mocksever中引入了spring cloud zuul做代理转发,如果请求的url和配置的mock规则匹配(精确匹配和模糊匹配),忽略,不做转发。如果mock配置的url和请求url是完全匹配的,没有问题。例如,请求url:http://localhost:8080/mock/test/query/12345

mock配置:

url response
/mock/test/query/12435 {"ret_code":"A00000"}

但是如果mock的配置url包含正则表达式:/mock/test/query/(\d.*),ZuulHandlerMapping类中的isIgnoredPath返回false,因为该方法调用的是AntPathMatcher的match方法,而AntPathMatcher的匹配规则是通配符匹配,无法识别正则表达式。

AntPathMatcher基本规则:

1、? 匹配一个字符(除过操作系统默认的文件分隔符)
2、* 匹配0个或多个字符
3、**匹配0个或多个目录
4、{spring:[a-z]+} 将正则表达式[a-z]+匹配到的值,赋值给名为 spring 的路径变量。

二、方案:

1、重写isIgnoredPath方法   ---------无法实现,isIgnoredPath是私有方法。

2、重写match方法  -----------可以实现,math是接口PathMatcher的方法。

三:具体实现

ZuulHandlerMapping源码:

/*
* Copyright 2013-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/ package org.springframework.cloud.netflix.zuul.web; import java.util.Collection; import javax.servlet.http.HttpServletRequest; import org.springframework.boot.autoconfigure.web.ErrorController;
import org.springframework.cloud.netflix.zuul.filters.RefreshableRouteLocator;
import org.springframework.cloud.netflix.zuul.filters.Route;
import org.springframework.cloud.netflix.zuul.filters.RouteLocator;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.handler.AbstractUrlHandlerMapping; import com.netflix.zuul.context.RequestContext; /**
* MVC HandlerMapping that maps incoming request paths to remote services.
*
* @author Spencer Gibb
* @author Dave Syer
* @author João Salavessa
* @author Biju Kunjummen
*/
public class ZuulHandlerMapping extends AbstractUrlHandlerMapping { private final RouteLocator routeLocator; private final ZuulController zuul; private ErrorController errorController; private PathMatcher pathMatcher = new AntPathMatcher(); private volatile boolean dirty = true; public ZuulHandlerMapping(RouteLocator routeLocator, ZuulController zuul) {
this.routeLocator = routeLocator;
this.zuul = zuul;
setOrder(-200);
} @Override
protected HandlerExecutionChain getCorsHandlerExecutionChain(HttpServletRequest request,
HandlerExecutionChain chain, CorsConfiguration config) {
if (config == null) {
// Allow CORS requests to go to the backend
return chain;
}
return super.getCorsHandlerExecutionChain(request, chain, config);
} public void setErrorController(ErrorController errorController) {
this.errorController = errorController;
} public void setDirty(boolean dirty) {
this.dirty = dirty;
if (this.routeLocator instanceof RefreshableRouteLocator) {
((RefreshableRouteLocator) this.routeLocator).refresh();
}
} @Override
protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
if (this.errorController != null && urlPath.equals(this.errorController.getErrorPath())) {
return null;
}
if (isIgnoredPath(urlPath, this.routeLocator.getIgnoredPaths())) return null;
RequestContext ctx = RequestContext.getCurrentContext();
if (ctx.containsKey("forward.to")) {
return null;
}
if (this.dirty) {
synchronized (this) {
if (this.dirty) {
registerHandlers();
this.dirty = false;
}
}
}
return super.lookupHandler(urlPath, request);
} private boolean isIgnoredPath(String urlPath, Collection<String> ignored) {
if (ignored != null) {
for (String ignoredPath : ignored) {
//pathMatcher 是私有变量,而且被初始化为AntPathMatcher对象
if (this.pathMatcher.match(ignoredPath, urlPath)) {
return true;
}
}
}
return false;
} private void registerHandlers() {
Collection<Route> routes = this.routeLocator.getRoutes();
if (routes.isEmpty()) {
this.logger.warn("No routes found from RouteLocator");
}
else {
for (Route route : routes) {
registerHandler(route.getFullPath(), this.zuul);
}
}
} }
pathMatcher 是私有变量,而且被初始化为AntPathMatcher对象,假设我们写一个接口PathMatcher的实现类,重写match方法,但是没有办法在isIgnoredPath中被调用到。
想到的是在ZuulHandlerMapping注入到容器的时候,把私有属性pathMatcher初始化成我们定义的新的实现类。 第一步:
修改注入ZuulHandlerMapping的配置类ZuulServerAutoConfiguration、ZuulProxyAutoConfiguration,新建2个类,copy上面2个类的代码,重新命名,例如命名为:
CusZuulServerAutoConfiguration、CusZuulProxyAutoConfiguration。把依赖的类也copy源码新建一个类,放到同一个包下,最后的目录结构如下:

圈出来的三个类都是需要的依赖类

第二步:
新建接口PathMatcher的实现类,重写math方法:
import org.springframework.util.AntPathMatcher;
import org.springframework.util.Assert;
import org.springframework.util.PathMatcher;
import org.springframework.util.StringUtils; import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern; public class RePathMatcher extends AntPathMatcher implements PathMatcher {
@Override
public boolean match(String pattern, String path) {
if(super.match(pattern,path )){
return true;
}
else {
pattern = pattern.replaceAll("\\*\\*","(.*)" );
Pattern rePattern = Pattern.compile(pattern);
Matcher matcher = rePattern.matcher(path);
if (matcher.matches()) {
return true;
}
return false;
} }
}

说明:先调用父类AntPathMatcher中的match方法,如果匹配到了就返回true,如果没有匹配到,就走正则,这里面需要先把pattern中的**替换成正则表达式,因为在配置的转发规则都是下面这样的形式:

path host
/tech/** http://tech.com

如果不替换,**无法被识别会抛异常。替换后就是正常的正则匹配了。

第三步:

使用反射修改ZuulHandlerMapping中的pathMatcher属性。

修改我们新建的配置类CusZuulServerAutoConfiguration中ZuulHandlerMapping注入的代码:

@Bean
public ZuulHandlerMapping zuulHandlerMapping(RouteLocator routes) {
ZuulHandlerMapping mapping = new ZuulHandlerMapping(routes, zuulController());
try {
Class<?> clazz = Class.forName("org.springframework.cloud.netflix.zuul.web.ZuulHandlerMapping");
Field[] fs = clazz.getDeclaredFields();
for (Field field : fs) {
field.setAccessible(true);
if(field.getName().equals("pathMatcher")){
field.set(mapping, new RePathMatcher());
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
mapping.setErrorController(this.errorController);
return mapping;
}

第四步:在SpringbootApplication中屏蔽ZuulProxyAutoConfiguration

@SpringBootApplication(exclude = ZuulProxyAutoConfiguration.class)

另外两个自定义的配置类需要修改:

@ConditionalOnBean(ZuulProxyMarkerConfiguration.Marker.class)
//替换成:
@ConditionalOnBean(annotation=EnableZuulProxy.class)
 
@ConditionalOnBean(ZuulServerMarkerConfiguration.Marker.class)
替换成
@ConditionalOnBean(annotation=EnableZuulProxy.class)
 
 

PS:目前想到的方式就是这样,如果大家有好的方式,欢迎留言。


修改ZuulHandlerMapping私有变量pathMatcher,重写match方法,使url匹配兼容正则表达式。的更多相关文章

  1. Nginx应用-Location路由反向代理及重写策略 请求转发-URL匹配规则 NGINX Reverse Proxy

    NGINX Docs | NGINX Reverse Proxy https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/ ...

  2. 采用重写tostring方法使ComboBox显示对象属性

    当ComboBox中添加的是对象集合的时候,如果运行就会发现显示是的命令空间.类名,而如果我们想显示对象属性名的时候,我们就可以在对象类中重写object基类中的tostring方法.

  3. spring使用注解通过子类注入父类的私有变量

    方法一 通过 super.setBaseDao方法设置父类私有变量 父类 public class BaseServiceImpl {    private BaseDao baseDao; publ ...

  4. JS 私有变量

    严格来讲,JS之中没有私有成员的概念:所以对象属性都是公有的.不过,倒是有一个私有变量的概念. 任何在函数中定义的变量,都可以认为是私有变量,因为不能在函数的外部访问这些变量. 私有变量包括函数的参数 ...

  5. Python小白学习之如何添加类属性和类方法,修改类私有属性

    如何添加类属性和类方法,修改类私有属性 2018-10-26  11:42:24 类属性.定义类方法.类实例化.属性初始化.self参数.类的私有变量的个人学习笔记 直接上实例: class play ...

  6. 改变图像,运用match方法判断

    <!DOCTYPE html><html><head> <meta charset="utf-8"> <title>菜鸟 ...

  7. JavaScript match()方法和正则表达式match()

    先介绍参数为普通字符串的使用方式,此时match方法的返回值是存放首次匹配内容的数组.如果没有找到匹配结果,返回null.语法结构: 1 str.match(searchvalue)参数解析:(1). ...

  8. 正则表达式之match方法

    一直以来,对正则表达式都是非常的恐惧的,以至于学习接口自动化时,到了正则,我就想放弃,于是乎,我将近有一个多月没有继续学习.某天睡醒,阳光正好,摊在床上冥想,我不能被眼前的坎挡住了我前进的路呀,说干就 ...

  9. 【原】iOS动态性(二):运行时runtime初探(强制获取并修改私有变量,强制增加及修改私有方法等)

    OC是运行时语言,只有在程序运行时,才会去确定对象的类型,并调用类与对象相应的方法.利用runtime机制让我们可以在程序运行时动态修改类.对象中的所有属性.方法,就算是私有方法以及私有属性都是可以动 ...

随机推荐

  1. p4475 巧克力王国

    传送门 分析 我们多维护一个值,代表某个点子树中所有点的权值和 于是如果某个点它的min和max乘a(/b)的值小于范围则直接把整个子树都加进去 估价函数就是这个点的子树中的理论最小值 代码 #inc ...

  2. 查看并解除Oracle锁

    当某个数据库用户在数据库中插入.更新.删除一个表的数据,或者增加一个表的主键时或者表的索引时, 常常会出现ora-00054:resource busy and acquire with nowait ...

  3. [GO]revoer的应用

    error的函数只是用来报一些低等级的错误,panic是报那些会导致程序崩溃的错误,但是会有一个问题就是panic也会导致程序中断 ,如果我们需要程序在报错之后继续运行并报出错误的信息 就需要使用到r ...

  4. 网站配置新的YII2可运行版本

    1. config 中的 gitignore 先将 -local 加入版本库 2. 将 runtime 目录添加写权限 3. 将web目录的asset 添加写的权限 4.

  5. Transferring Data Between ASP.NET Web Pages

    14 July 2012 20:24 http://www.mikesdotnetting.com/article/192/transferring-data-between-asp-net-web- ...

  6. static 和final

    1.static       static关键字可以用来修饰类的变量,方法和内部类.static是静态的意思,也是全局的意思,它定义的东西属于全局,与类相关,不与具体实例相关.就是说它调用的时候,只是 ...

  7. 敏捷软件开发:原则、模式与实践——第11章 DIP:依赖倒置原则

    第11章 DIP:依赖倒置原则 DIP:依赖倒置原则: a.高层模块不应该依赖于低层模块.二者都应该依赖于抽象. b.抽象不应该依赖于细节.细节应该依赖于抽象. 11.1 层次化 下图展示了一个简单的 ...

  8. 编写高质量代码改善C#程序的157个建议——建议149:使用表驱动法避免过长的if和switch分支

    建议149:使用表驱动法避免过长的if和switch分支 随着代码变得复杂,我们很容易被过长的if和switch分支困扰. 一个类枚举类型Week如下: enum Week { Monday, Tue ...

  9. WorkFlow 工作流 学习笔记

    传统ERP为制造业企业产供销人财物的管理提供了一整套优化企业资源利用,集物流.信息流.资金流为一体的现代化管理工具.但是它在过程集成和企业间集成方面存在不足.具体表现在: 1.传统ERP是一个面向功能 ...

  10. mybatis所需pom文件内容以及配置文件

    官方网站http://www.mybatis.org/mybatis-3/zh/index.htmlpom文件<?xml version="1.0" encoding=&qu ...