Swagger异常定位纪实,是用的不对,仍是Swagger自己设计问题

前言

swagger ui是一个采用注解驱动的接口文档工具,目前已支持标准的open api v3规范协议,因此不只能够在java项目里使用,每一个语言都有相应的open api实现。项目集成swagger后,能够生成导出open api v3格式化的元数据集,有了这个接口元数据,你能够在任何支持v3协议的ui上展现你的api信息。在先后端分离的项目中,swagger ui的出现,大大提升了先后端联调的效率。swagger ui在解析注解标注的元数据信息时,特别场景下会抛异常,并且抛的异常没有直观的有价值的异常信息,因此深刻的debug了一番,虽然最后问题解决很简单,可是过程很是曲折。故将bug定位过程记录在此。java

异常信息

image
这个异常只会在加载swagger-ui的页面时会抛出,每次刷新页面,获取一次api接口就会触发一次异常。git

异常分析

@JsonProperty("x-example")
public Object getExample() {
    if (example == null) {
        return null;
 }
    try {
        if (BaseIntegerProperty.TYPE.equals(type)) {
            return Long.valueOf(example);
 } else if (DecimalProperty.TYPE.equals(type)) {
            return Double.valueOf(example);
 } else if (BooleanProperty.TYPE.equals(type)) {
            if ("true".equalsIgnoreCase(example) || "false".equalsIgnoreCase(defaultValue)) {
                return Boolean.valueOf(example);
 }
        }
    } catch (NumberFormatException e) {
        LOGGER.warn(String.format("Illegal DefaultValue %s for parameter type %s", defaultValue, type), e);
 }
    return example;
}

如上是异常相关的代码。从异常信息表象来看,是一个强转致使的问题,代码试图将一个空的字符串转换成数值类型致使异常抛出。而且是getExample时抛出的异常,这里须要了解swagger ui的加载过程和基础架构才能直接定位。swagger中的example是为了在生成的api doc中,给出相关字段的调用示例,并在触发接口调用时,默认自动填充example的值。这里显然是哪一个地方的example设置不合理致使的异常。那么,接下来要作的就是找到这个空字符串的原始代码。github

debug找到真实缘由

借助IDEA的debug功能,点击异常后面的create breakpoint,在触发异常的地方打上断点。触发异常,进入断点,获取到了关键信息
image
一个被描述为app id的字段,用这个信息全局搜索,获得以下的结果:
image
有三个相关的Model实体,首先,这三个Model的appId字段都没有设置过example属性,因此,到这一步,能够先下一个小的结论,不是咱们设置的example致使的问题,默认在不设置的状况下,example的默认值就是空字符串。而后确定只有其中一个有问题,由于异常只会触发一次。在不知道结果状况下,依次对这三个Model的appId字段加上正确的example描述,经测试,只有GetAppBannerRequestDTO加上时,异常才消失,罪魁祸首就是它了。可是,为何呢?其余两个Model为啥就没有问题呢?在博主交叉测验后,发现了最终的缘由。后端

结论及注意事项

当Model做用于请求的接收参数时,而且请求的类型为GET,那么Swagger Ui会自动收集Model全部属性的examole参数,由于这个参数是字符串类型,因此会作一个类型转换动做。当字段类型为数值类型,又有没手动设置example的值,那么Swagger框架拿到的是个空字符串,强转空字符串就抛异常了。而若是请求是POST,就不会触发这段逻辑,因此同为携带数值类型DTO的ImgReplaceRequestDTO没有问题。若是不是接收参数,做为响应参数,也不会触发这段逻辑,故而AppBannerResponseVO也就没有问题了。因此,须要注意的就是当DTO做用于GET请求的接收参数时,切记给全部的数值类型加上正确的example属性api

后记

博主认为这里属于一个设计缺陷,而不是咱们的使用问题。在获取example的逻辑里,第一段代码就判断了example是否为null。这代表了example有可能为空,可是默认值却设置了一个空字符串。表明不手动将example设置为null,这段判null返回的逻辑就永远跑不到,并且没人会这么作,手动给example设置为null。何况,在触发异常的这种场景下,框架不能强制使用者设置example这种操做。在github仓库追踪这块代码发现,目前Swagger ui已经迈入了3.x版本,全面基于open api v3协议规范设计。因此,这部分代码彻底不同了。而存档的1.5x版本这个问题依旧。架构

下面是3.x的处理方式,虽然example的默认值仍是“”。可是经过NotBlank判断了下,因此不会触发异常了
imageapp

为啥不直接升级3.x?

3.x版本既然已经修复了,为啥不直接升级到3.x版本呢?可能有人会有这个疑问。Swagger3.x版本属于一个大跨度的迭代版本,和以前的版本彻底不兼容,3.x主要面向了open api v3规范协议设计实现,注解实体等模型都是一一对应的。而在这个版本以前的1.5x系列版本是Swagger本身设计的api模型。因此代码层上面彻底不兼容,升级的工做量会很是大。不过,新项目仍是推荐使用3.x版本,这个版本的api数据更通用。能够根据api的数据生成各类语言的客户端包。就像proto生成客户端包同样。框架