背景
项目用到了Swagger API框架,便于调用方迅速了解接口。
[关于Swagger详见Swagger:Rest API的描述语言]
其他组同事反馈我们组的Swagger API描述跟实际接口不一致,造成不小的困扰。
查看源代码
@POST
@Path("/getValueList")
@ApiOperation(value = "获取所有值", response = GetOtherResponse.class)
GetValueResponse getValueList(@ApiParam(value = "GetValueRequest ", required = true) @Valid GetValueRequest req);
实际返回值GetValueResponse ,Swagger注解返回值却是GetOtherResponse
解决方法
考虑到代码量庞大,代码风格多样,这种问题不会是单个,采用github上一款开源的 语法树解析 工具来检查代码。
找出所有API描述跟实际返回值不一致的方法。
Maven依赖
首先导入maven依赖
<dependency>
<groupId>com.github.javaparser</groupId>
<artifactId>javaparser-core</artifactId>
<version>2.2.1</version>
</dependency>
工具类
工具类用来获取所有的方法声明
将Java源代码解析成抽象语法树。
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashSet;
import java.util.Set;
import com.github.javaparser.JavaParser;
import com.github.javaparser.ParseException;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.visitor.VoidVisitorAdapter;
public class GetAllMethod extends VoidVisitorAdapter {
private CompilationUnit cu;
private Set<MethodDeclaration> allEmement = new HashSet<>();
public GetAllMethod(InputStream in) throws Exception
{
try {
cu = JavaParser.parse(in);
} finally {
in.close();
}
}
public GetAllMethod(String path) {
try {
cu = JavaParser.parse(Files.newInputStream(Paths.get(path)));
} catch (ParseException | IOException e) {
e.printStackTrace();
}
}
public CompilationUnit getCompilationUnit() {
return cu;
}
public String getSourceCode() {
return cu.toString();
}
public Set<MethodDeclaration> getAllMethod() {
this.visit(cu, null);
return allEmement;
}
@Override
public void visit(MethodDeclaration n, Object arg) {
allEmement.add(n);
}
}
解析方法
由于代码中有详细注释,这里不做过多描述。
通过Debug查看解析后的AST抽象语法树结构对象,一步步获取要检查的对象。
import com.github.javaparser.ast.Node;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.expr.AnnotationExpr;
import com.github.javaparser.ast.expr.MemberValuePair;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
public class CheckReponse {
public static void main(String[] args) throws Exception {
Path p = Paths
.get("接口所在的文件夹");
// 1.遍历包下面所有的文件
Files.list(p).filter(path -> {
return !Files.isDirectory(path);
}).forEach(file -> {
try {
// 2. 检查每一个文件
checkMethod(Files.newInputStream(file));
} catch (Exception e) {
e.printStackTrace();
}
});
}
private static void checkMethod(InputStream in) throws Exception {
// 3. 获取所有的方法声明
GetAllMethod getMethodUtil = new GetAllMethod(in);
for (MethodDeclaration m : getMethodUtil.getAllMethod()) {
String response = m.getType().toString();
// 4. 取得方法体上所有的注解
List<AnnotationExpr> list = m.getAnnotations();
for (AnnotationExpr annotation : list) {
// 5. 取得 swagger ApiOperation注解
if ("ApiOperation".equalsIgnoreCase(annotation.getName().toString())) {
List<Node> childNode = annotation.getChildrenNodes();
// 6. 继续遍历注解的子节点
for (Node n : childNode) {
if (n instanceof MemberValuePair) {
MemberValuePair memberValuePair = (MemberValuePair) n;
// 7. 获取 swagger 的response 名字
if ("response".equalsIgnoreCase(memberValuePair.getName())) {
// 8. 比对方法体的response跟swagger 的response是否一致
if (!memberValuePair.getValue().toString().contains(response)) {
// 9. 打印出声明不一致的swagger注解
System.out.println(
"method = " + m.getName() + "; response = " + response + " ; ApiOperation ="
+ memberValuePair.getValue());
}
}
}
}
}
}
}
}
}