swagger注解规范前后端沟通成本

前后端沟通成本

随着开发深入,前后端接口沟通表现出问题,swagger作为接口调试,但是控制层参数和返回值swagger表现不佳,还得在javadoc下看接口的描述,参数含义,返回值描述,前端反应效率太低,现在要利用swagger达到接口文档查看,调试双重目的。其实swagger提供了接口文档功能,只是后端开发没有明确要求,没有编写swagger合适标签。

1 接口分组

我们在Spring Boot中定义各个接口是以Controller作为第一级维度来进行组织的,Controller与具体接口之间的关系是一对多的关系。我们可以将同属一个模块的接口定义在一个Controller里。默认情况下,Swagger是以Controller为单位,对接口进行分组管理的。这个分组的元素在Swagger中称为Tag,但是这里的Tag与接口的关系并不是一对多的,它支持更丰富的多对多关系。

1.1 默认分组

首先,我们通过数据中心的例子,来看一下默认情况,Swagger是如何根据Controller来组织Tag与接口关系的。首先看下默认Swagger接口显示:

78DA2887-66A9-4F74-905E-78E3614AD8D6.png
图中标出了Swagger默认生成的Tag与Spring Boot中Controller展示的内容与位置。

1.2 自定义默认分组的名称

接着,我们可以再试一下,通过@Api注解来自定义Tag,比如这样:

D2DBDC3F-0FE2-4EB3-B955-0B9DA5F67591.png
再次启动应用之后,我们就看到了如下的分组内容,代码中@Api定义的tags内容替代了默认产生的tag名称.

989A73F7-58F9-4899-AEBF-8A4CAD54F42B.png

1.3 合并控制层接口

我们现在看两个控制层,生成的内容:
image.png
我们会发现这两个控制层Water Resource Controller, Water Resource Property Controller其实都是水资源管理范畴的,前端应该看到的是一组水资源接口,那么应该把两个控制层合并给前台。

image.png

1.4 细粒度的接口分组

我们应该按照模块分组给前端提供接口,这样对接更加方便,前后端可以针对模块为单位沟通,不需要在各个控制层查找模块需要的接口;其实就是精确到某个接口的合并,比如将数据中心“河流列表”模块包含中所有接口分组(不是全部接口)。这时候我们可以通过使用@ApiOperation注解中的tags属性做更细粒度的接口分类定义,比如上面的需求就可以这样子写:

@RestController
@RequestMapping("/api/waterResource")
public class WaterResourceControler {

    @ApiOperation(value = "获取水资源类型列表", tags = "河流列表")
    @GetMapping(value = "/type")
    public ResponseEntity getWaterResourceType() {
        return ResponseEntity.ok().body(waterResourceService.initTopTreeVMS());
    }

    @ApiOperation(value = "获取二级水资源级别列表", tags = "河流列表")
    @GetMapping(value = "/level")
    public ResponseEntity getWaterResourceLevel() {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("waterResourceLevels", CommonEnum.RegionLevel.getLevels());
        return ResponseEntity.ok().body(jsonObject);
    }

    @ApiOperation(value = "获取水资源树型列表", tags = "河流列表")
    @GetMapping(value = "/tree")
    public ResponseEntity getTreeVM(@RequestParam String type) {
        List<WaterResourceTreeVM> waterResourceTreeVMS = waterResourceService.getWaterResourceTreeVM(type);
        return new ResponseEntity<>(waterResourceTreeVMS, HttpStatus.OK);
    }
}    
复制代码

效果如下图所示:

image.png
我们应该有意识的按模块给前端提供接口,养成习惯后,前后端按模块够沟通接口,以后模块发生变化,模块接口统一调整。
同一个接口可以属于多个模块,比如河流列表接口,那么怎么操作呢?相信你一定已经发现tags属性其实是个数组类型:


我们可以在类通过tags定义多个的分组名达到这个要求:

    @ApiOperation(value = "获取水资源类型列表", tags = {"河流列表", "河流数据"})
    @GetMapping(value = "/type")
    public ResponseEntity getWaterResourceType() {
        return ResponseEntity.ok().body(waterResourceService.initTopTreeVMS());
    }
复制代码

查看效果图:

image.png

3 RequestPart,requestParam,MapAttribute

@RequestPart

    @PostMapping("/icons")
    @ApiOperation(value = "新建图标")
    public ResponseEntity<IconDTO> createIcon(@RequestPart MultipartFile multipartFile, @RequestParam String name) {
    ...
}
复制代码

@RequestPart这个注解用在multipart/form-data表单提交请求的方法上,支持的请求方法的方式MultipartFile,属于Spring的MultipartResolver类。这个请求是通过http协议传输的。但是如果参数里还有其他参数,就继续加@RequestParam注解参数。如果参数比较多就要传VM对象,但是这里不能使用通常的@RequestBody注解,java认不出来,不过有一个办法,使用@MapAttribute,可以把VM对象转换成普通@RequestParam参数:

    @PostMapping("/icons")
    @ApiOperation(value = "新建图标", notes = "新建图标成功后,返回IconDTO;" + "fileNoExistent异常:新建图标文件不存在")
    public ResponseEntity<IconDTO> createIcon(@RequestPart MultipartFile multipartFile, @ModelAttribute IconCreateVM iconCreateVM) {
    ...
}
复制代码

这样swagger就可以区别开,效果如下图:

image.png
点击Try it out!,发出请求成功!查看请求详情:

.../api/icon/manage/icons?application=WATER_DISASTERS&classify=FLOOD&geoType=BUILD&name=abc&nameC=%E6%B5%8B%E8%AF%95
复制代码

IconCreateVM的确被拆成了@RequestParma参数。