还记得上次我写过一篇关于实际项目代码分层和规划的文章《看完这篇,别人的开源项目结构应该能看懂了》, 在文尾处提到过一些注意事项,其中第一条就是:java
私信里居然有不少小伙伴提问说,为何不能这样作?后端
我内心暗自寻思:难道这么作的小伙伴都没有被同事捶吗?(滑稽)markdown
得嘞,今天我们就掰扯掰扯这件事,这是实际写代码时常忽略的一个问题app
我本身曾经接手过一个前人留下来的老项目,拿到代码,导入IDEA的那一刻,我哭出了声。工具
由于它的Controller
层代码都是相似这样写的:oop
@RestController @RequestMapping("/index") public class IndexController { // 获取App首页内容 @PostMapping("/getIndexContent") public ResponseWrapper getIndexContent( @RequestBody Map<String, Object> paramMap ) { ResponseWrapper res = new ResponseWrapper(); // 下面开始作传参有效性的校验 if (!paramMap.containsKey("article_id")) { res.setCode(500); res.setMsg("缺乏 article_id 信息"); return res; } if (!paramMap.containsKey("page")) { res.setCode(500); res.setMsg("缺乏 page 信息"); return res; } if (!paramMap.containsKey("size")) { res.setCode(500); res.setMsg("缺乏 size 信息"); return res; } if (!paramMap.containsKey("version")) { res.setCode(500); res.setMsg("缺乏 version 信息"); return res; } // ...... 此处省略 } // ...... 此处省略 } 复制代码
别的咱先不说,竟然明目张胆地在Controller
层里方法里用Map
传参?!简直丧心病狂了。幸好下面还有一波传参有效性的验证,对于传递的参数,我好歹也能猜个大概,否则那真是喵了个咪了。测试
接下来,咱们就好好唠一唠:为何不要在Controller
层传参时使用Map
类型!spa
正好,这地方有一个咱小伙伴活生生的例子。插件
记得以前有个小伙伴提问,问过一个这样的问题,说他接手了一个别人的老项目,问了我一个相似这样的问题:3d
看到没!
用Map
传参的第一个(也是最大的一个)弊端就是:这会致使后续接手和维护的人怀疑本身的人生,由于他根本不知道代码传的啥参数,想要构造参数去调试接口只能靠脑补、摸瞎、以及猜想了。
试想一下,其实咱们代码里任何一个地方的传参均可以使用Map
来传,若是真的这么作了,代码中连任何数据模型类都不须要定义了,果然如此的话,这样的代码咱能看懂吗?
并且这位小伙伴接手的项目竟然还用的是LinkedHashMap
参数,能够说很秀了。
除此以外,紧接着还会带来下面这个问题。
我以前写过一篇文章《先后端都分离了,该搞个好用的API管理系统了!》,聊过如今市面上一些比较好用的、能极大提高先后端开发效率的API管理工具,这对于先后端开发来讲,简直是莫大的福音。
咱们就以Swagger
这个API工具为例,若是Controller
传参使用Map
的话:
// 获取App首页内容 @ApiOperation("获取App首页内容") @PostMapping("/getIndexContent") public ResponseWrapper getIndexContent( @RequestBody Map<String, Object> paramMap ) { // ...... 此处省略 } 复制代码
则API
工具没法读取具体参数项目和参数类型,因此传参什么的也看不出来:
换言之,我若是将上面的Map
传参改成自定义数据模型类IndexQueryDto
来传参的话:
// 获取App首页内容 @ApiOperation("获取App首页内容(改造后)") @PostMapping("/getIndexContent") public ResponseWrapper getIndexContent( @RequestBody IndexQueryDto indexQueryDto ) { // ...... 此处省略 } 复制代码
@ApiModel(value = "App首页内容请求参数实体对象") class IndexQueryDto { @ApiModelProperty(value = "文章ID号") @NotNull(message = "缺乏 article_id 信息") private Long article_id; @ApiModelProperty(value = "页面数") @NotNull(message = "缺乏 page 信息") private Integer page; @ApiModelProperty(value = "每页条目数") @NotNull(message = "缺乏 size 信息") private Integer size; @ApiModelProperty(value = "App版本号") @NotNull(message = "缺乏 version 信息") private String version; // ...... 此处省略set/get方法 } 复制代码
则相似Swagger
这种API
工具就很是方便地能帮助咱们管理参数了:
这样不论是本身调试,仍是前、后端对接口都会方便得多。
同理,除了Swagger
这种API
管理工具以外,像在个人前文《没用过这些IDEA插件?怪不得写代码头疼》中推荐过的一个很是好用的接口管理插件RestfulToolkit
也没法识别出Map
类型所盛放的具体参数:
可是对于数据模型的定义参数,就能很是清晰的给出参数细节,并方便地提供接口测试:
仍是以文章开头举例的代码来讲,无论怎么样,写这段代码的哥们仍是负责的,毕竟兢兢业业地用手工连环if()
判断完成了全部参数的有效性校验:
但问题是,咱们真的须要这种辣眼睛的手工连环if()
判断来作参数校验吗?
一样在前文《啥?据说你还在手写复杂的参数校验?》中也说过了,咱们其实能够经过注解来方便地规避繁杂的参数校验工做,但前提是不能使用Map
类型传参,须要使用数据模型的定义,就像这样:
class IndexQueryDto { @NotNull(message = "缺乏 article_id 信息") private Long article_id; @NotNull(message = "缺乏 page 信息") private Integer page; @NotNull(message = "缺乏 size 信息") private Integer size; @NotNull(message = "缺乏 version 信息") private String version; // ...... 此处省略get/set方法 } 复制代码
一个NotNull
注解便可搞定,它不香吗?
有些小伙伴表示用Map
传参的好处就是能够随意扩展,后期变更灵活,想往里面塞几个参数就塞几个参数;并且也省去了各类对象定义和命名的烦恼。
若是实在不能避免用Map
传参,也麻请配备完备的测试用例吧,免得让后来接手维护的人每天看着代码怀疑人生了。
经过测试用例,后来接手维护的人也能快速搞清代码间的参数传递和调用,否则真的只能靠脑补画面去调试了。
好了,说了这么多,若是你项目的Controller
层代码还在使用Map
传参的话,答应我,二话别说,赶快所有偷偷去改掉,快!速度!跑步前进!