滥用@PathVariable导致bug原因分析解决

目录
  • 前言
  • 复现
  • 3个匹配步骤
    • 1,根据Path精准匹配
    • 2,如果精准匹配没有成功,就开始模糊匹配
    • 3,如果模糊匹配还匹配不上,就返回null
  • 最后

前言

最近测试同学反馈,上周上线的一个功能会偶然性的报404,按理说这个功能在测试环境已经测试通过,也在线上运行了好几天,怎么会突然报错呢。

一开始以为是前端同学请求的接口有误,但是测试又说只是偶然性的404,几率也不高,于是打开日志找到对应的接口,一眼看到了接口上定义的@PathVariable,再一看参数,基本就确定是开发同学为了偷懒又误用@PathVariable导致的了。

先说结论吧:@PathVariable可以使请求参数动态的绑定到URL上,但是如果请求参数中包含特殊字符,比如 /,就可能导致Spring匹配到一个错误的URL,或者匹配不到合适的URL。

复现

下面,我用一个简单的伪代码复现一下这个bug,与大家分析一下这个bug发生的原因,以及如何解决,最后顺便再通过源码加深一下印象。

如下,我们定义一个接口,并且通过@PathVariable将入参动态的绑定到URL上。

@RestController
@RequestMapping(value = "/demo")
public class DemoController {
    @GetMapping(value = "/getVal/{val}")
    public ResponseEntity<Object> getVal(@PathVariable String val){
        System.out.println("参数:" + val);
        return ResponseEntity.ok(val);
    }
}

然后我们测试一下这个接口:

正常情况下,我们输入一个普通无特殊符号的参数,控制台也成功打印了出来。

但是业务参数往往是不可控的,比如当参数变成“ hello/world”时,代码就不能正常执行了。

大家可以从图中看到,Spring将原本预期的URL:/demo/getVal/{val},解析成了/demo/getVal/hello/world。

而之所以测试同学最近才发现这个接口有问题,也正是因为上线之初并没有遇到带有/的参数,所以接口看起来是正常的,直到最近在生产环境遇到了一个带/的参数。

正确的做法是:将URL定义为/demo/getVal,然后将参数通过表单或者query的方式传递。

解决的办法很简单,相信有点经验的同学都能很快将这个问题修复。

但是知其然,更要知其所以然,顺着这个问题,我们探究一下Spring究竟是如何解析URL的。

首先,我们找到Spring webmvc的包,在org.springframework.web.servlet.handler包下找到AbstractHandlerMethodMapping类,这个类就是会将我们定义的mapping和URL绑定起来。

这个类中的lookupHandlerMethod方法,会查找当前请求的最佳匹配处理程序方法,并且如果找到多个匹配项,就选择最佳匹配项。

分析这个方法,我们可以得到这样3个匹配步骤

3个匹配步骤

1,根据Path精准匹配

2,如果精准匹配没有成功,就开始模糊匹配

3,如果模糊匹配还匹配不上,就返回null

至此,一个URL解析的过程就完毕了。单看源码可以发现,逻辑其实并不复杂,就是一个按照规则不断匹配的过程。

最后

总得来说,@PathVariable可以让我们开发接口的时候省去一些功夫,但是需要注意到,如果绑定的参数带有特殊字符,就有可能导致非预期的bug。

一般来说,@PathVariable比较适合绑定整型的参数,如果是字符串类的参数,建议大家还是通过表单或json的方式传参。

以上就是滥用@PathVariable导致bug原因分析解决的详细内容,更多关于@PathVariable导致bug的资料请关注我们其它相关文章!

(0)

相关推荐

  • spring mvc中的@PathVariable动态参数详解

    目录 spring mvc @PathVariable动态参数 spring mvc是如何做到根据参数名动态绑定参数的? 反射获取参数名 -parameters参数 -g参数 ASM SpringMVC的处理方式 总结 spring mvc @PathVariable动态参数 spring mvc中的@PathVariable是用来获得请求url中的动态参数的,十分方便 @Controller public class TestController { @RequestMapping(value

  • RestFul风格 — 使用@PathVariable传递参数报错404的解决

    目录 @PathVariable传递参数报错404 restFul风格传参, 参数中带斜杠/问题 @PathVariable传递参数报错404 代码: @RequestMapping("/test1/{a}/{b}") public String test1(@PathVariable int a, @PathVariable int b, Model model){ int res=a+b; model.addAttribute("msg",res); retur

  • @PathVariable和@RequestParam传参为空问题及解决

    目录 @PathVariable和@RequestParam传参为空 小结一下 使用@pathvariable与@requestparam碰到的问题 1.@pathvariable 2.@requestparam @PathVariable和@RequestParam传参为空 @RestController public class UserController { @GetMapping(value = {"/xie/{name}","/xie"}) public

  • Java中的三种校验注解的使用(@Valid,@Validated和@PathVariable)

    目录 @Valid和@Validated @Valid和@Validated比较 @Valid高级使用 @Valid级联校验 @Validated高级使用 @Validated分组校验 @Validated分组校验顺序 @Validated非实体类校验 @PathVariable 正则表达式校验 继承BasicErrorController类 自定义校验注解 @Valid和@Validated @Valid和@Validated比较 相同点: @Valid注解和 @Validated注解都是开启

  • 关于动态参数使用@PathVariable的解析

    目录 动态参数使用@PathVariable解析 在Controller层的代码如下 获取路径中的参数值@PathVariable中的value 1.@PathVariableStringname 2.@PathVariable(“username”)Stringname 动态参数使用@PathVariable解析 现在有如下的一条超链接 <a href="<c:url value=" rel="external nofollow" /actions/a

  • 使用@PathVariable注解如何实现动态传值

    目录 @PathVariable注解实现动态传值 动态参数使用@PathVariable解析 @PathVariable注解实现动态传值 动态传值 @RequestMapping(value="/Test/{id}") public void Test(@PathVariable Integer id){ ............. } 用法 在页面表单的action中,写controller中对应的方法名 TestController.java @RequestMapping(val

  • 滥用@PathVariable导致bug原因分析解决

    目录 前言 复现 3个匹配步骤 1,根据Path精准匹配 2,如果精准匹配没有成功,就开始模糊匹配 3,如果模糊匹配还匹配不上,就返回null 最后 前言 最近测试同学反馈,上周上线的一个功能会偶然性的报404,按理说这个功能在测试环境已经测试通过,也在线上运行了好几天,怎么会突然报错呢. 一开始以为是前端同学请求的接口有误,但是测试又说只是偶然性的404,几率也不高,于是打开日志找到对应的接口,一眼看到了接口上定义的@PathVariable,再一看参数,基本就确定是开发同学为了偷懒又误用@P

  • ShareSDK造成App崩溃的一个BUG原因分析以及Fix方法

    近期研究了一下Game App做社交分享,最后选择了ShareSDK来集成,不仅是因为ShareSDK支持国内外主流社交平台,更重要的是ShareSDK提供了专门的 cocos2d-x集成方案,有专门的文档和代码Demo供开发者参考. 文档中提到了三种集成方式:纯Java方式.plugin-x方式以及Cocos2d-x专用组件方式,这里选择了ShareSDK Cocos2d-x专用组件(v2.3.7版本)的方式.按照文档中描述的步骤进行的相对顺利,在各个社交平台的appkey生效后,我们对dem

  • android4.0混淆XmlPullParser报错原因分析解决

    复制代码 代码如下: [2013-05-20 17:30:52 - danielinbiti] Proguard returned with error code 1. See console [2013-05-20 17:30:52 - danielinbiti] Note: there were 67 duplicate class definitions. [2013-05-20 17:30:52 - danielinbiti] Warning: library class android

  • MyBatis-Plus自动填充功能失效导致的原因及解决

    1:先检查 字段有没有加上注解 @TableField(fill = FieldFill.INSERT_UPDATE) @TableField(fill = FieldFill.INSERT_UPDATE) private Date updatedTime; 2:有没有实现 MetaObjectHandler 接口 ,并且加入到 Spring 容器中 @Component public class MyMetaObjectHandler implements MetaObjectHandler

  • Vue动态设置图片时src不生效的原因及解决方法

    目录 原因分析 解决方法 import和require的区别 原因分析 在vue项目中动态设置img的src时,图片会加载失败.我们可以先看个例子. <template> <div> <h1>动态设置图片</h1> <div> <h5>图片一</h5> <img :src=" logoFlag === 'vue' ? '../assets/vue-logo.png' : '../assets/react-l

  • 导致MyEclipse内存不足的原因分析及解决办法

    1.修改eclipse.ini 在Myeclipse安装目录下G:\MyEclipse8.5\Genuitec\MyEclipse 8.5有一个myeclipse.ini配置文件,设置如下: -vmargs -Xmx512m -XX:MaxPermSize=256m -XX:ReservedCodeCacheSize=64m 2.设置Default VM Arguments 在myEclipse中,打开Windows-> Preferences->Java->Installed JREs

  • 不规范使用ThreadLocal导致bug分析解决

    目录 因为线程重用导致的信息错乱的bug 正确使用的姿势 更优雅的处理方式 最后 因为线程重用导致的信息错乱的bug ThreadLocal一般用于线程间的数据隔离,通过将数据缓存在ThreadLocal中,可以极大的提升性能.但是,如果错误的使用Threadlocal,可能会引起不可预期的bug,以及造成内存泄露. 有时我们会在一个接口中缓存某些数据到ThreadLocal中,但是我们要意识到,处理请求的这些线程是由tomcat提供的,而tomcat提供的线程都是配置在一个线程池中的. 也就是

  • 在Yii2中使用Pjax导致Yii2内联脚本载入失败的原因分析

    当我用defunkt/jquery-pjax载入Yii2的ActiveForm时发生一个错误,正常情况下是 ActiveForm的两个js应该先载入,而实际情况是 typeError:JQuery(...).yiiActiveForm is not a function. 在github的issues对这个问题已经讨论并得到了解决. Pjax首先通过html()执行内联的<script>,然后才通过executeScriptTags()执行带着src的<script>,所以导致找不

  • Android Force Close 出现的异常原因分析及解决方法

    一.原因: forceclose,意为强行关闭,当前应用程序发生了冲突. NullPointExection(空指针),IndexOutOfBoundsException(下标越界),就连Android API使用的顺序错误也可能导致(比如setContentView()之前进行了findViewById()操作)等等一系列未捕获异常 二.如何避免 如何避免弹出Force Close窗口 ,可以实现Thread.UncaughtExceptionHandler接口的uncaughtExcepti

  • Ajax向后台传json格式的数据出现415错误的原因分析及解决方法

    问题描述: ajax往后台传json格式数据报415错误,如下图所示 页面代码 function saveUser(){ var uuId = document.getElementById("uuid").value; var idCard = document.getElementById("idCard").value; alert(uuId+idCard); // var result = new Object(); // result.uuId = uuI

随机推荐