源码剖析@ApiImplicitParam对@RequestParam的required属性的侵入性

问题起源

使用SpringCloud构建项目时,使用Swagger生成相应的接口文档是推荐的选项,Swagger能够提供页面访问,直接在网页上调试后端系统的接口, 非常方便。最近却遇到了一个有点困惑的问题,演示接口示例如下(原有功能接口带有业务实现逻辑,这里简化了接口):

/**
 * @description: 演示类
 * @author: Huang Ying
 **/
@Api(tags = "演示类")
@RestController
@Slf4j
public class DemoController {
   

	@ApiOperation(value = "测试接口")
	@ApiImplicitParams({
   
			@ApiImplicitParam(name = "uid", value = "用户ID", paramType = "query", dataType = "Long")
	})
	@RequestMapping(value = "/api/json/demo", method = RequestMethod.GET)
	public String auth(@RequestParam(value = "uid") Long uid) {
   
		System.out.println(uid);
		return "the uid: " + uid;
	}
}

问题出在接口参数uid的必填性上,@RequestParam注解里require默认为true,要求必填,但@ApiImplicitParam注解里require默认为false,要求非必填,该业务接口在进行功能联调时,uid居然能得到一个null值,按照一般认知习惯@ApiImplicitParam注解的主要作用是生成接口文档,不应该对@RequestParam的属性有侵入性才对,目前反馈的bug,让我怀疑@ApiImplicitParam是不是会侵入@RequestParam的require属性?

框架选型、版本及主要功能

项目搭建

SpringBoot版本:2.1.6.RELEASE
SpringCloud版本:Greenwich.SR3

业务模块

SpringCloud业务模块使用的swagger:

swagger bootstrap ui 1.9.6 增强swagger ui样式
spring4all-swagger 1.9.0.RELEASE 配置化swagger参数,免去代码开发

业务网关

SpringCloud业务网关使用的swagger:

knife4j 2.0.1 增强swagger ui样式(网关用gateway搭建,swagger使用knife4j-spring-boot-starter依赖,可以聚合业务模块的swagger文档)

此次的范围只针对SpringCloud业务模块,暂时不涉及业务网关的Swagger文档。

测试工具

测试工具目前有两个:
swagger doc:使用浏览器进行访问,如下图:

postman:手动配置接口参数,示例:

案例实战

接口测试1

接口示例如开篇所示,我们先使用如下接口,全部使用默认值,即@ApiImplicitParam的required为false,@RequestParam的required为true:

@ApiOperation(value = "测试接口")
@ApiImplicitParams({
   
		@ApiImplicitParam(name = "uid", value = "用户ID", paramType = "query", dataType = "Long")
})
@RequestMapping(value = "/api/json/demo", method = RequestMethod.GET)
public String auth(@RequestParam(value = "uid") Long uid) {
   
	System.out.println(uid);
	return "the uid: " + uid;
}

看swagger的结果:

看postman的结果:

接口测试2

我们修改@ApiImplicitParam的required值为true,@RequestParam不变,重启模块
@ApiImplicitParam(name = "uid", value = "用户ID", paramType = "query", required = true, dataType = "Long")

看swagger的结果:

通过调试浏览器可以发现,为空校验是js完成的,js判断为空后,并未发起请求到后端,这样我们可以认为swagger内@ApiImplicitParam的required参数生效了。

接口测试3

在前面我们使用postman测试接口时,发现参数项是空的,我们加上参数,但不写值测试后,结果让人诧异:

并且无论@ApiImplicitParam的required值如何修改,结果都是一样的,肯定有一个地方是搞错了,导致我们误判。

后来仔细查阅资料,发现是我们对@RequestParam的required参数理解错了,这个required为true的含义是:接口参数名一定要存在,但参数后面有没有值它管不着。拿刚刚的例子来说:

这两个请求是通过的:
localhost:8080/api/json//demo?uid
localhost:8080/api/json//demo?uid=

只有这种请求是不通过的:
localhost:8080/api/json//demo?
小结论

经过上述三个接口的测试场景,我们至少可以明确3点:

  1. @ApiImplicitParam的required参数不会对@RequestParam的required值造成侵入,它们俩不相关。
  2. @ApiImplicitParam的required参数会影响swagger doc的js逻辑判断,为空校验是在js层面上完成的。
  3. @RequestParam的required参数默认情况下只会校验是否有该参数名,不校验它是否有值。

源码剖析

swagger部分

上一节当中提及swagger读取@ApiImplicitParam注解的required参数,最终会体现在js上,通过浏览器F12的追踪,定位到swaggerbootstrapui.js文件上,这里摘抄部分源码:

# 点击发送按钮时,逐行读取参数信息,并提取required参数
 paramBody.find("tr").each(function () {
   
    var paramtr=$(this);
    var cked=paramtr.find("td:first").find(":checked").prop("checked");
    var _urlAppendflag=true;
    //that.log(cked)
    if (cked){
   
        //如果选中,留意此行的required:paramtr.data("required")信息提取
        var trdata={
   name:paramtr.find("td:eq(2)").find("input").val(),in:paramtr.data("in"),required:paramtr.data("required"),type:paramtr.data("type"),emflag:paramtr.data("emflag"),schemavalue:paramtr.data("schemavalue")};
        //that.log("trdata....")
        //that.log(trdata);
        //获取key
        //var key=paramtr.find("td:eq(1)").find("input").val();
        var key=trdata["name"];
        //获取value
        var value="";
        var reqflag=false;
        // 后面代码省略
    }
  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值