页面访问swagger-ui.html报错 java.lang.NumberFormatException 定位思路

489 阅读4分钟

问题描述

在访问swagger页面时,我们应用会在控制台输出一个java.lang.NumberFormatException: For input string: ""的报错信息,这个报错的信息是把字符串转换为数字类型时,Java的parseLongparseInt等方法报错

image.png 出现这个错误后一时找不到头脑,Google也找不到什么有用的信息,只能去从调用源头一步一步分析堆栈信息

首先定位到swagger的controller吧,毕竟他也得产出一个JSON前端才可以响应。我们发现swagger是使用JSON去序列化数据内容的,那么问题就很明了了。问题的步骤大致如下:

  1. swagger扫描所有的Java Bean,Java Controller,组装一个Map的数据结构
  2. 组装完成后,调用JackSon去序列化这些Java Bean的属性、注释等
  3. 使用Spring MVC框架返回这些数据

image.png

字段定位

字段的定位是一个艰辛的过程,因为我们的微服务项目也是上百个接口的,接口的参数都是使用的Java Bean去接收。如果是整体的单体项目。或许有可能上千,并且通过复杂的模块化管理,你很可能找不到是哪个Bean的注解出了问题,毕竟这么多的Bean,谁也不会去用脑子去记。

从报错中,发现是io.swagger.models.parameters.AbstractSerializableParameter.getExample(AbstractSerializableParameter.java:412) 调用的JDK的函数,那我们直接从这里入手

image.png 在这里debug后,功夫不负有心人定位到了,很可惜,只有一个字段名,而且groupId是我们的业务中比较常见的字段名,心想这下完犊子了,可能之后要与这个报错共存了😂

image.png

不找到问题是不可能滴,这辈子都不可能滴。用脑筋想想,Jackson调用序列化单个属性时,它一定能清楚认识到自己要序列化哪个对象。就好像你要牵女神的手,你一定知道你女神是谁一样。相反,swagger想让Jackson牵它的手,那就一定会留下一个水晶鞋,那就让我们来找下我们这次swagger为Jackson留下的水晶鞋

image.png

找到相应函数后,我们来分析具体的问题所在。

分析问题产生

io.swagger.models.parameters.AbstractSerializableParameter#getExample这个函数中,转换和序列化属性时**,取swagger注解的的example属性用""转换为数字时,出现的问题。**经Google查阅后,这个类配合的属性则为:@ApiModelProperty("分组id")注解的各个值的记录

image.png

那让我们查看下这个字段,为什么会报错呢?

看着似乎也没什么问题

@ApiModelProperty("分组id") 
private Long groupId;

那再看下example字段吧 哦,原来是我们的字段没有这个example字段,它的default又是"",造成的这个问题

public @interface ApiModelProperty {
    String value() default "";
    String example() default ""; 
}

分析到这里,问题已经很明了了,就是我们没有增加example属性才导致的,那我们加上就可以啦。

@ApiModelProperty(value = "分组id",example = "1")
private Long groupId;

等等,解决问题后我们推敲下发现。为什么别的接口不加不会报错,这个接口不加怎么报错了呢?

原来这个函数没有增加 @RequestBody,那为什么没加就报错?

@GetMapping 
public void goodsOrderExport(QueryBody queryBody) throws Exception {}

通过查阅发现:

 application/json 时,io.swagger.models.parameters.AbstractSerializableParameter 会实例化参数,也就是通过example的值为属性赋值,如果example没有显示赋值,就是空串"",但是如果实体类用作 application/json 那么就不会走这个方法去实例化参数。

牵手成功,双双退场

目前有三种解决方案

  1. 把项目里面的使用这种属性的情况,也就是在@ApiModelProperty注解里面写上 example = “1”,其实就是给出一个示例,具体写1还是2或3都无所谓,但要注意一点是 example 对应的值必须是Integer类型的。如2.2中一样。
@ApiModelProperty(value = "分组id",example = "1")
private Long groupId;
  1. 接口修改为Body传输,哈哈。似乎不怎么聪明的样子
@GetMapping
public void goodsOrderExport(@RequestBody QueryBody queryBody) throws Exception {}
  1. 既然存在这种问题,官方开发者不会没有发现吧,然后就去看了看swagger-models的其他版本,发现在swagger-models:1.5.20版本是springfox-swagger2:2.9.2里面依赖的版本,既然是依赖,那就自己引用了一个swagger-models:1.5.21的版本,然后看看源码,发现 在新的版本中进行了改正。于是我把swagger-models:1.5.20版本排除了,然后单独引入了swagger-models:1.5.21版本。

image.png

参考文章

blog.csdn.net/weixin_4429…

www.cnblogs.com/shisanye/p/…