文档生成插件使用指南
根据 Spring MVC Controller 下方法生成接口文档
先看示例代码
UserController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| package cn.gudqs.business.docer.controller;
import cn.gudqs.business.docer.dto.request.ListUserRequest; import cn.gudqs.business.docer.dto.response.BaseResponse; import cn.gudqs.business.docer.dto.response.ListUserResponse; import cn.gudqs.util.JsonUtils; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList; import java.util.List;
@Slf4j @RestController @RequestMapping("/api/v1/user") public class UserController {
@PostMapping("/listUser") public BaseResponse<List<ListUserResponse>> listUserPage(ListUserRequest listUserPage) { log.info("listUserPage--> "+ JsonUtils.getJsonString(listUserPage));
return BaseResponse.success(new ArrayList<>(), 0L); }
}
|
BaseResponse
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| package cn.gudqs.business.docer.dto.response;
import lombok.Data;
@Data public class BaseResponse<T> {
private Integer code;
private String message;
private Boolean success;
private T data;
private Long totalCount;
public static <T> BaseResponse<T> success() { return success(null); }
public static <T> BaseResponse<T> success(T data) { return success(data, 0L); }
public static <T> BaseResponse<T> success(T data, Long totalCount) { BaseResponse<T> baseResponse = new BaseResponse<>(); baseResponse.setSuccess(true); baseResponse.setCode(0); baseResponse.setMessage("success"); baseResponse.setData(data); baseResponse.setTotalCount(totalCount); return baseResponse; }
}
|
ListUserResponse
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| package cn.gudqs.business.docer.dto.response;
import lombok.Data;
import java.util.Date;
@Data public class ListUserResponse {
private String nickName;
private String realName;
private String phoneNumber;
private Integer userGender;
private Integer userAge;
private String userAvatar;
private Date lastLoginTime;
}
|
ListUserRequest
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| package cn.gudqs.business.docer.dto.request;
import lombok.Data; import lombok.EqualsAndHashCode;
import java.util.Date;
@Data @EqualsAndHashCode(callSuper = true) public class ListUserRequest extends BasePageRequest {
private String searchKeyword;
private Integer gender;
private Integer ageStart;
private Integer ageEnd;
private Date loginTimeStart;
private Date loginTimeEnd;
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| package cn.gudqs.business.docer.dto.request;
import lombok.Data;
@Data public class BasePageRequest {
private Integer pageNumber;
private Integer pageSize;
}
|
说明
以上都是大家常写的分页代码,此时我在 listUserPage
这个方法上右键,弹出菜单中选择:生成Api接口文档(restful),如图
然后就会得到一个弹框,当然如果你根据提示点了 OK
,则直到下次重启,都不会再出现
弹框中内容如下,此内容区域可滚动预览,全选复制粘贴
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
| # 查询用户信息(分页) ## 请求信息
### 请求地址 ''' http://192.168.0.104:8080/api/v1/user/listUser '''
### 请求方法 ''' POST '''
### 请求体类型 ''' application/x-www-form-urlencoded '''
## 入参 ### 入参示例(Postman==> Bulk Edit) '''json pageNumber:1 pageSize:20 searchKeyword:模糊搜索词<br>支持用户昵称, 用户姓名, 用户手机号 gender:2 ageStart:0 ageEnd:0 loginTimeStart:过滤登录时间范围-开始 loginTimeEnd:过滤登录时间范围-结束 '''
### 入参字段说明 | **字段** | **类型** | **必填性** | **含义** | **其他信息参考** | | -------- | -------- | -------- | -------- | -------- | | pageNumber | **Integer** | 否 | 分页-当前页码 | | | pageSize | **Integer** | 否 | 分页-分页大小 | | | searchKeyword | **String** | 否 | 模糊搜索词<br>支持用户昵称, 用户姓名, 用户手机号 | | | gender | **Integer** | 否 | 用户性别<br>0: 保密<br>1: 男<br>2: 女 | | | ageStart | **Integer** | 否 | 过滤年龄范围-起始 | | | ageEnd | **Integer** | 否 | 过滤年龄范围-结束 | | | loginTimeStart | **Date** | 否 | 过滤登录时间范围-开始 | | | loginTimeEnd | **Date** | 否 | 过滤登录时间范围-结束 | |
## 出参 ### 出参示例 '''json { "code": 0, "message": "错误信息", "success": false, "data": [ { "nickName": "用户昵称", "realName": "用户姓名", "phoneNumber": "110", "userGender": 2, "userAge": 1024, "userAvatar": "https://mp.weixin.com/p/jewagheiajifejgihewjg.ifew", "lastLoginTime": "最近一次登录时间" } ], "totalCount": 0 } '''
### 返回字段说明 | **字段** | **类型** | **含义** | **其他信息参考** | | -------- | -------- | -------- | -------- | | code | **Integer** | 否 | 状态码<br>0: 代表成功<br>-1: 代表未知异常<br>\>0: 代表已知异常 | | | message | **String** | 否 | 错误信息 | | | success | **Boolean** | 否 | 是否成功<br>true: 代表成功, 此时 code = 0<br>false: 代表失败, 此时 code != 0 | | | data | **List\<ListUserResponse\>** | 否 | 返回数据 | | |└─ nickName | **String** | 否 | 用户昵称 | | |└─ realName | **String** | 否 | 用户姓名 | | |└─ phoneNumber | **String** | 否 | 用户手机号 | | |└─ userGender | **Integer** | 否 | 用户性别<br>0: 保密<br>1: 男<br>2: 女 | | |└─ userAge | **Integer** | 否 | 用户年龄 | | |└─ userAvatar | **String** | 否 | 用户头像地址 | | |└─ lastLoginTime | **Date** | 否 | 最近一次登录时间 | | | totalCount | **Long** | 否 | 分页-总条数 | |
|
由于文章本身就是Markdown格式,所以上面中 ```
我用 '''
代替,我用截图来展示下效果:截图使用 Chrome 插件打开 HTML 滚动截屏得到,HTML 是由 typora 生成的!
总结
如果有多个方法,想一次性生成,则可在 UserController 上右键,点击生成Api文档(restful) ,如图:
此时生成的文档,就是多个方法单独生成的文档的简单的拼接。
根据 Spring MVC Controller 的接口信息导出到 Postman
项目视图选择类、包,点右键
还是沿用上面的代码,但是考虑到单个方法单独导出到 Postman 非常费时费力,可用性不高,因此方法级别的右键导出,并没有实现!但是单个文件级别,还是OK的,只需在文件导航视图找到 UserController 类,右键,点击 导出Api到Postman即可!如图
另外这个右键,其实不管是类,包,还是文件夹,乃至根目录,都可以点击导出到 Postman,只是导出的范围不同!
由于范围比较大,因此过滤了非 @Controller、@RestController 注解的类,可放心使用。
点击导出后,会将 Postman 的json 生成到项目下的 api-doc/postman 文件夹,如图
打开Postman点击 File –> Import
此时我们打开 Postman,使用快捷键 Ctrl + O
或 Command + O
, 接着拖拽文件过去,或点击 UploadFiles,弹框选择文件打开,如图
最终得到一个 Collection
如图:不管是请求,返回示例,文档,或者说 url 变量,body和参数处理,都帮你搞定了,你只需要修改下参数的实际值(如果使用 #example,则这步也可以省略),然后点蓝色的 Send 按钮就 ok 了(千万别忘记启动项目)!
这里简单说下,url 的组成
1.http:// 这个本地开发一般不用 https 对吧,嘿嘿
2.ip: 这个使用代码遍历网卡,过滤掉 ipv6,127.0.0.1 后得出的局域网 ip,如果没搞虚拟机啥的,一般获取比较准确,要还是错了,点击 Collection 名称,在 Variables 下修改 NaN 的 Current Value 就好了
3.port: 这个默认8080,然后支持 Spring Boot 项目,即读取 application.yml/application.properties 文件获取 server.port 的值,嗯~~ 若存在 application-xxx 这种情况,还真不好取,只能读取 spring.profiles.active 但是也未必准确,毕竟有很多地方支持设置。理论性idea的东西都可以获取,但idea的插件开发文档找不到这方面的信息,大概是不会花精力搞了,毕竟错了直接改变量就好了,改一处,所有请求生效。
4.剩下的部分就是 Controller 的 @RequestMapping 注解 + 方法的 @RequestMapping(或@GetMapping之类)注解的值拼凑起来。若方法为 GET,则参数会以?xxx=xxx&xx=xx 形式拼接上去,也可在 Params 下查看
总结
总得来说,这个插件还是太简单了,其实不用使用指南,知道在哪右键起什么作用,就OK了,因为处了右键点击,其他一概不用你管(包括复制)!可谓是偷懒极了。。。。。
说下这个插件的发展史。
一开始是用于微服务的接口方法,根据参数和返回值来生成文档给其他开发看,当时还是自己写了个 Util,然后传 Class 作为参数来解析得到参数、返回值等信息,最终生成文档,当时的所有中文备注,都是通过 Swagger 的注解来实现的,没办法,运行时注释早干掉了,得不到。
后来觉得这东西可以抽出来,不要被项目束缚,先是想到了idea插件,但是发现 Api 及其不同,idea中也无法获取 Class,也就是当时写的代码无法复用,于是又考虑 maven 插件,发现这个是可以取到 Class,进行复用的。
写完后,又发现使用这个插件,必须设置参数,也就是你要生成那个类的文档,很麻烦啊这,于是想到之前看idea插件的一些api可以获取到这些信息,干脆来了个结合,idea右键获取Class 的全限定名,传给maven插件,maven插件来生成,最后idea弹框展示。其实这样也不错,但是又有个巨大的问题,就是maven插件其实依赖于 target 文件中的信息,这部分必须 compile 后才有,使得每次运行插件都要先跑compile 再生成,本来maven运行速度就慢,这样就更慢了。加上进度条后还是感觉体验极差!!!
最终,在某个夜黑风高的晚上,我死活睡不着觉,干脆打开电脑,点击备忘录,记下明天摸鱼时间要写个纯idea插件,然后躺下数羊!啊那是不可能的。
不过这插件最终确实都是在闲的没活干的摸鱼时间写的,最终把原来代码拷贝一份到idea插件项目中,一边参照一边使用idea提供的Api修改代码,最终得到了纯idea插件版本!
之后遇到了比较坑的点就是泛型的解析和参数的解析,目前都解决了,可以说是随便怎么写,都能识别了。比如最上面的示例代码的 T data
,这还是小 case,如果改成 List<T>
呢? 如果再来一个带泛型的类 PageInfo<T>
,而我加个字段 PageInfo<T> pageData;
来把 T 传给 PageInfo 呢,解析会有问题吗?刚开始会,后来解决了!!!只能说我自己都快绕晕了,但最终还是解决了。这其实值得复盘的,不过我这人比较懒,就不搞了。其实这种情况繁多的时候,要写代码不能只考虑部分情况,或是来一种情况加一种情况,必须找到通用的方法,当然未必什么时候都有通用的方法,不过还是要去追求的!!
后续还会和大家分享一些我插件中的核心代码,包括idea插件提供的api,自己百度找的工具类等等。
另外我上面说的微服务那个接口文档,其实就是不带(restful)后缀的菜单选项,其生产的文档不含有HTTP协议相关的信息,而是纯粹的入参、出参、code含义等。另外文件视图右键有三个选项,另外另个就是批量生成 Markdown文件,毕竟一个一个复制粘贴很累的,而很多笔记软件都支持批量导入Markdown文件,这样就不就简单了。