不宜直接传参,传参数量不宜过多
@RestController
@RequestMapping("/index")
public class IndexController {
// 获取App首页内容
@PostMapping("/getIndexContent")
public ResponseWrapper getIndexContent(
@RequestParam("articleId") String articleId),
@RequestParam("page") int page)
@RequestParam("size") int size)
@RequestParam("version") String version)) {
ResponseWrapper res = new ResponseWrapper();
// 下面开始做传参有效性的校验
if (null==articleId) {
res.setCode(500);
res.setMsg("缺少 article_id 信息");
return res;
}
if (null==page) {
res.setCode(500);
res.setMsg("缺少 page 信息");
return res;
}
if (null==size) {
res.setCode(500);
res.setMsg("缺少 size 信息");
return res;
}
if (null==version) {
res.setCode(500);
res.setMsg("缺少 version 信息");
return res;
}
// ...... 此处省略
}
// ...... 此处省略
}
这样代码就是直接写死的状态,后续想要加减参数修改参数名都需要修改接口,违反开放封闭原则,而且参数太多看着很乱,更重要的是,很容易穿错顺序,比如size和page,一不注意就传反了,那么针对这个现象可以把方法修改为Map传参
@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;
}
String articleId = paramMap.containsKey("article_id").toString();
String page = paramMap.containsKey("page").toString();
String size = paramMap.containsKey("size").toString();
String version = paramMap.containsKey("version").toString();
// ...... 此处省略
}
// ...... 此处省略
}
虽然解决了直接传参的问题,但是又引入新的缺陷。
程序中最好不要使用Map传参,尤其是Contorller层参数传递建议不要使用HashMap,推荐使用数据模型定义
因为参数不确定,需要根据后续代码去猜入参,这就是一件很痛苦的事情了,如果文档和测试用例缺失,
没有参数校验那就真的是一场灾难了
功能确实是完成了,有没有更优雅的实现方式呢
我们来分析一下这段代码,其实这部分完成的功能就是获取前台的参数信息,写了这么多行。而且其中if 写了这么多,典型的代码中具有“坏味道”的特征
我们可以如何修改它呢
这里可以用到Java8的一个新特性Optional 类。Optional 类主要解决的问题是臭名昭著的空指针异常(NullPointerException) —— 每个 Java 程序员都非常了解的异常。
改写后
@RestController
@RequestMapping("/index")
public class IndexController {
// 获取App首页内容
@PostMapping("/getIndexContent")
public ResponseWrapper getIndexContent( @RequestBody Map<String, Object> paramMap ) {
ResponseWrapper res = new ResponseWrapper();
String articleId = Optional.ofNullable(paramMap.containsKey("article_id").toString()).orElse("default");
String page = Optional.ofNullable(paramMap.containsKey("page").toString()).orElse("0");
String size = Optional.ofNullable(paramMap.containsKey("size").toString()).orElse("10");
String version = Optional.ofNullable(paramMap.containsKey("version").toString()).orElse("1");
// ...... 此处省略
}
// ...... 此处省略
}
可以看到代码精简了许多,而且构筑了默认值,保证了后续数据的完整性,这样就没有问题了吗,
显然不是,刚才仅仅解决了参数校验问题,还是没有解决map中有哪些参数,参数的具体类型是int还是String的问题
如果有类似于Swagger这种API工具,调用界面
什么也看不出来,如果没有测试用例瞬间就会问候原作者,所以为了少让接手人骂我们,传参的时候最好先定义一个参数实体。
@ApiModel(value = "App首页内容请求参数实体对象")
class IndexQueryDto {
@ApiModelProperty(value = "文章ID号")
@NotNull(message = "缺少 article_id 信息")
private String articleId;
@ApiModelProperty(value = "页面数")
@NotNull(message = "缺少 page 信息")
private int page;
@ApiModelProperty(value = "每页条目数")
@NotNull(message = "缺少 size 信息")
private int size;
@ApiModelProperty(value = "App版本号")
@NotNull(message = "缺少 version 信息")
private String version;
@ApiModelProperty(value = "补充参数")
private Map<String ,Object> featureMap;
}
// 获取App首页内容
@ApiOperation("获取App首页内容(改造后)")
@PostMapping("/getIndexContent")
public ResponseWrapper getIndexContent( @RequestBody IndexQueryDto indexQueryDto ) {
// ...... 此处省略
}
如此这般,又能简洁代码,又能保证数据完整性。如果担心扩展或者以后编程老项目不好修改实体可以预先加一个补充参数Map方便后期扩展。
这样是不是认为传参已经很完美了,但是其实还有还可以完善,针对以上的例子,如果限定文章id号必须为十位,而且只能是有字母打头,内容为数字字母怎么实现呢
@ApiModel(value = "App首页内容请求参数实体对象")
class IndexQueryDto {
String pattern = "[A-Za-z][A-Za-z0-9]{9}";
@ApiModelProperty(value = "文章ID号")
@NotNull(message = "缺少 article_id 信息")
private String articleId;
@ApiModelProperty(value = "页面数")
@NotNull(message = "缺少 page 信息")
private int page;
@ApiModelProperty(value = "每页条目数")
@NotNull(message = "缺少 size 信息")
private int size;
@ApiModelProperty(value = "App版本号")
@NotNull(message = "缺少 version 信息")
private String version;
@ApiModelProperty(value = "补充参数")
private Map<String ,Object> featureMap;
// 为了简化这里就只写一个参数
public IndexQueryDto(String articleId){
if(isValied(articleId)){
this.articleId = articleId;
}
}
private boolean isValied(String articleId){
articleId.matches(pattern);
}
}
// 获取App首页内容
@ApiOperation("获取App首页内容(改造后)")
@PostMapping("/getIndexContent")
public ResponseWrapper getIndexContent( @RequestBody IndexQueryDto indexQueryDto ) {
// ...... 此处省略
}
这样就完成了参数的完整性和健壮性的校验,也就是DDD领域驱动模型的
第一节Domain Primitive,充血模型的设计思想。
参考:https://zhuanlan.zhihu.com/p/125520876
https://www.bilibili.com/video/BV11q4y1q74f?spm_id_from=333.999.0.0