将一个@RequestMapping定义的方法映射为两个http服务—-记一次有趣的排查问题过程

Java并发包源码学习系列:ReentrantReadWriteLock读写锁解析

小伙伴遇到个问题,某个controller发布的http服务直接访问没问题,通过nginx转发后就报404,此模块其他url访问都正常。。
 
controller代码如下:

@RequestMapping("/addrExport.spr")
public class AddrExportController
@RequestMapping(params = "method=exportDynamicQueryData")
public void exportDynamicQueryData(HttpServletRequest request, HttpServletResponse response, String downLoadData) {
     System.out.println("hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh");
     .....
} 

springboot应用,未配置上下文根。所以此http服务未使用nginx转发时的直接访问地址为:http://localhost:8299/addrExport.spr?method=exportDynamicQueryData
 
前台ajax调用url:

 
nginx映射路径:

server { listen 8888; ...... location /portal/space-addr/ { proxy_pass http://localhost:8299/space-addr/;
 } ..... }

 

使用nginx端口访问的url如下,就是此url访问404 http://localhost:8888/portal/space-addr/addrExport.spr?method=exportDynamicQueryData   一阵兵荒马乱,小伙伴发现此url转发到后台的路径为 http://localhost:8299/space-addr/addrExport.spr?method=exportDynamicQueryData 多了一个space-addr   此时有两种方案, 方案1.在controller里@RequestMapping时增加space-addr前缀 方案2.修改nginx映射去掉proxy_pass中多余的前缀,注意红色字体部分去掉了space-addr,见下文:

location /portal/space-addr/ {
    proxy_pass http://localhost:8299/;
}

采用方案2修改nginx配置验证后发现确实能解决问题。但问题来了。。。。 修改nginx转发规则后,此模块原来正常访问的的功能应该报错才对,但事实上这些请求顺畅无比,仿佛世界从未发生改变,也就是说这些url在nginx转发规则增加space-addr和去掉space-addr时都可以正常访问。这真的不科学。。。   。。此处省略走过的弯路。。。   抓包查看了这些访问正常的功能对应的请求,发现都是一样的url,只是参数不同:http://localhost:8888/portal/space-addr/rescommon/service/callServerFunction 查看此url对应的controller(ResCommonServiceController,隐藏特别深,由公共模块提供,见下图)发现:类上无@RequestMapping注解,也无@Controller或者@RestController注解,方法上此注解的value=callServerFunction。而根据nginx转发规则,此注解的value至少应该包含rescommon/service/callServerFunction。那么缺失的rescommon/service是在哪里被拼接的呢?

 

感谢spring的日志输出,偶然搜索发现了如下内容,什么鬼,竟然增加了两个
rescommon/service/callServerFunction相关的requestmapping:一个有space-addr前缀,一个无前缀。。
  最终发现公共侧对ResCommonServiceController做了特殊处理,具体见CommonConfig类的registerCommonServerMapping方法(见下图),默认会生成两个requestmapping: 1./rescommon/service/callServerFunction 2.${pub.commonservice.prefix}/rescommon/service/callServerFunction,对于排查问题的这个模块存在配置pub.commonservice.prefix=space-addr,最终效果为space-addr/rescommon/service/callServerFunction 正是因为生成了两个requestmapping,所以才会出现nginx里转发规则配或不配space-addr都没问题的情况,因为都能匹配到requestmapping。。

 

.NET 云原生架构师训练营(模块二 基础巩固 敏捷开发)–学习笔记

 类全文如下:

 1 @Configuration
 2 public class CommonConfig {
 3 
 4     @Value("${pub.commonservice.prefix}")
 5     String prefix;
 6 
 7     @Bean
 8     public ResCommonServiceController registerCommonServiceController() {
 9         return new ResCommonServiceController();
10     }
11 
12     @Autowired
13     public void registerCommonServerMapping(ResCommonServiceController registerCommonServiceController, RequestMappingHandlerMapping mapping) {
14         String uri = "/rescommon/service/";
15         RequestMappingInfo requestMappingInfo = RequestMappingInfo.paths(uri).build();
16         ResRequestMappingHandlerMapping resRequestMappingHandlerMapping = new ResRequestMappingHandlerMapping(mapping, requestMappingInfo);
17         resRequestMappingHandlerMapping.detectHandlerMethods(registerCommonServiceController);
18         if (!("".equals(prefix) || prefix == null || "${pub.commonservice.prefix}".equals(prefix))) {
19             String[] split = prefix.split(",");
20             for (String prefix1 : split) {
21                 ResRequestMappingHandlerMapping resRequestMappingHandlerMapping1 = new ResRequestMappingHandlerMapping(mapping, RequestMappingInfo.paths(prefix1 + uri).build());
22                 resRequestMappingHandlerMapping1.detectHandlerMethods(registerCommonServiceController);
23             }
24         }
25     }
26 }

这个写法很有趣,第一次见。。自定义的ResRequestMappingHandlerMapping类扩展了spring的RequestMappingHandlerMapping,覆写了detectHandlerMethods方法,在registerMapping时将自定义的url前缀和method上的url做拼接。

 1 public class ResRequestMappingHandlerMapping extends RequestMappingHandlerMapping {
 2 
 3     /**
 4      * 代理对象
 5      */
 6     private RequestMappingHandlerMapping requestMappingHandlerMapping;
 7 
 8     /**
 9      * 存放类的RequestMapping信息
10      */
11     RequestMappingInfo typeMapping;
12 
13     public ResRequestMappingHandlerMapping(RequestMappingHandlerMapping requestMappingHandlerMapping, RequestMappingInfo typeMapping) {
14         this.requestMappingHandlerMapping = requestMappingHandlerMapping;
15         this.typeMapping = typeMapping;
16     }
17 
18     @Override
19     public void detectHandlerMethods(final Object handler) {
20         if(handler == null){
21             return;
22         }
23         Class<?> handlerType = (handler instanceof String ? getApplicationContext().getType((String) handler) : handler.getClass());
24         final Class<?> userType = ClassUtils.getUserClass(handlerType);
25 
26         Map<Method, RequestMappingInfo> methods = MethodIntrospector.selectMethods(userType, new MethodIntrospector.MetadataLookup<RequestMappingInfo>() {
27             public RequestMappingInfo inspect(Method method) {
28                 try {
29                     return getMappingForMethod(method, userType);
30                 }
31                 catch (Exception ex) {
32                     throw new IllegalStateException("Invalid mapping on handler class [" + userType.getName() + "]: " + method, ex);
33                 }
34             }
35         });
36 
37         if (logger.isDebugEnabled()) {
38             logger.debug(methods.size() + " request handler methods found on " + userType + ": " + methods);
39         }
40         for (Map.Entry<Method, RequestMappingInfo> entry : methods.entrySet()) {
41             Method invocableMethod = AopUtils.selectInvocableMethod(entry.getKey(), userType);
42             RequestMappingInfo mapping = entry.getValue();
43             if (typeMapping != null) {
44                 mapping = typeMapping.combine(mapping);
45             }
46             requestMappingHandlerMapping.registerMapping(mapping, handler, invocableMethod);
47         }
48     }

 

至此,谜题解开了,因为公共侧对ResCommonServiceController类做了特殊定制,使其发布的@RequestMapping方法发布了两个http服务。。所以nginx的转发规则带不带前缀都不影响功能使用。。

Redis 设计与实现 10:五大数据类型之有序集合

给TA买糖
共{{data.count}}人
人已赞赏
经验教程

前端面试:Http协议与浏览器

2021-1-15 21:44:00

经验教程

Java并发包源码学习系列:ReentrantReadWriteLock读写锁解析

2021-1-15 23:06:00

⚠️
免责声明:根据《计算机软件保护条例》第十七条规定“为了学习和研究软件内含的设计思想和原理,通过安装、显示、传输或者存储软件等方式使用软件的,可以不经软件著作权人许可,不向其支付报酬。”您需知晓本站所有内容资源均来源于网络,仅供用户交流学习与研究使用,版权归属原版权方所有,版权争议与本站无关,用户本人下载后不能用作商业或非法用途,需在24个小时之内从您的电脑中彻底删除上述内容,否则后果均由用户承担责任;如果您访问和下载此文件,表示您同意只将此文件用于参考、学习而非其他用途,否则一切后果请您自行承担,如果您喜欢该程序,请支持正版软件,购买注册,得到更好的正版服务。 本站为个人博客非盈利性站点,所有软件信息均来自网络,所有资源仅供学习参考研究目的,并不贩卖软件,不存在任何商业目的及用途,网站会员捐赠是您喜欢本站而产生的赞助支持行为,仅为维持服务器的开支与维护,全凭自愿无任何强求。本站部份代码及教程来源于互联网,仅供网友学习交流,若您喜欢本文可附上原文链接随意转载。
无意侵害您的权益,请发送邮件至 momeis6@qq.com 或点击右侧 私信:momeis 反馈,我们将尽快处理。
0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
今日签到
有新私信 私信列表
搜索