Swagger优化
当没有@RequestBody描述参数的时候如何让Swagger显示Model
开始时以为在SwaggerConfig中配置即可,后来发现Swagger没有对应的可配置项。
开始在网上搜索解决方案,最终搜索到:
https://blog.csdn.net/Driver_tu/article/details/100031407
但上面的解决方案是用另一个注解去描述对应的参数,我就在想可不可以不用任何注解,有无@RequestBody,Swagger的都是按Model默认展示的
我找到了关于springfox的源码解析
https://www.xiaominfo.com/2019/05/20/springfox-0/
我结合了上面两篇文章中的内容做出了修改。
1、OperationModelsProvider类重写
OperationModelsProvider的作用是收集参数Model
原来的类代码:
private void collectParameters(RequestMappingContext context) {
...
for (ResolvedMethodParameter parameterType : parameterTypes) {
if (parameterType.hasParameterAnnotation(RequestBody.class)
|| parameterType.hasParameterAnnotation(RequestPart.class)) {
ResolvedType modelType = context.alternateFor(parameterType.getParameterType());
LOG.debug("Adding input parameter of type {}", resolvedTypeSignature(modelType).or("<null>"));
context.operationModelsBuilder().addInputParam(modelType);
}
}
...
}
可以看到只有RequestBody和RequestPart描述的参数的type被加入context的model中。
修改后:
for (ResolvedMethodParameter parameterType : parameterTypes) {
// 添加判断无RequestBody注解不会被拦下
if (parameterType.hasParameterAnnotation(RequestBody.class)
|| !parameterType.hasParameterAnnotation(RequestBody.class)
|| parameterType.hasParameterAnnotation(RequestPart.class)) {
ResolvedType modelType = context.alternateFor(parameterType.getParameterType());
LOG.info("Adding input parameter of type {}", resolvedTypeSignature(modelType).or("<null>"));
context.operationModelsBuilder().addInputParam(modelType);
}
}
无RequestBody注解的参数也可以添加到model中
2、OperationParameterReader类重写
原来的代码:
private List<Parameter> readParameters(final OperationContext context) {
...
if (shouldExpand(methodParameter, alternate)) {
parameters.addAll(
expander.expand(
new ExpansionContext("", alternate, context.getDocumentationContext())));
} else {
parameters.add(pluginsManager.parameter(parameterContext));
}
...
}
private boolean shouldExpand(final ResolvedMethodParameter parameter, ResolvedType resolvedParamType) {
return !parameter.hasParameterAnnotation(RequestBody.class)
&& !parameter.hasParameterAnnotation(RequestParam.class)
&& !parameter.hasParameterAnnotation(PathVariable.class)
&& !isBaseType(typeNameFor(resolvedParamType.getErasedType()))
&& !enumTypeDeterminer.isEnum(resolvedParamType.getErasedType())
&& !isContainerType(resolvedParamType)
&& !isMapType(resolvedParamType);
}
在第一段代码的判断中如果下面的判断为true就会把参数创建一个ExpansionContext对象并加入,但是一般含有@RequestBody注解是不会走true这段,而是下面的pluginsManager.parameter(),所以我们将不带有RequestBody注解的参数也添加判断走false
修改后的代码(仅修改shouldExpand判断)
private boolean shouldExpand(final ResolvedMethodParameter parameter, ResolvedType resolvedParamType) {
// 有无RequestBody注解都返回false
return !parameter.hasParameterAnnotation(RequestBody.class)
&& parameter.hasParameterAnnotation(RequestBody.class)
&& !parameter.hasParameterAnnotation(RequestParam.class)
&& !parameter.hasParameterAnnotation(PathVariable.class)
&& !isBaseType(typeNameFor(resolvedParamType.getErasedType()))
&& !enumTypeDeterminer.isEnum(resolvedParamType.getErasedType())
&& !isContainerType(resolvedParamType)
&& !isMapType(resolvedParamType);
}
3、重写ParameterTypeReader类
从上面的代码可以知道,我们让参数走了pluginsManager.parameter()方法,找到这个方法他使用ParameterBuilderPlugin来处理,这个就是针对parameter的处理插件。
找到ParameterBuilderPlugin的实现类ParameterTypeReader,他主要来处理参数的类型,找到其中的处理方法findParameterType()
public static String findParameterType(ParameterContext parameterContext) {
ResolvedMethodParameter resolvedMethodParameter = parameterContext.resolvedMethodParameter();
ResolvedType parameterType = resolvedMethodParameter.getParameterType();
parameterType = parameterContext.alternateFor(parameterType);
//Multi-part file trumps any other annotations
if (isFileType(parameterType) || isListOfFiles(parameterType)) {
return "form";
}
if (resolvedMethodParameter.hasParameterAnnotation(PathVariable.class)) {
return "path";
} else if (resolvedMethodParameter.hasParameterAnnotation(RequestBody.class)) {
return "body";
} else if (resolvedMethodParameter.hasParameterAnnotation(RequestPart.class)) {
return "formData";
} else if (resolvedMethodParameter.hasParameterAnnotation(RequestParam.class)) {
return queryOrForm(parameterContext.getOperationContext());
} else if (resolvedMethodParameter.hasParameterAnnotation(RequestHeader.class)) {
return "header";
} else if (resolvedMethodParameter.hasParameterAnnotation(ModelAttribute.class)) {
LOGGER.warn("@ModelAttribute annotated parameters should have already been expanded via "
+ "the ExpandedParameterBuilderPlugin");
}
if (!resolvedMethodParameter.hasParameterAnnotations()) {
return queryOrForm(parameterContext.getOperationContext());
}
return "body";
}
在swagger展示页面中我们可以看到显示model的参数其parameter in都是body
结合上面的代码,我们可以看到没有注解描述的参数,最后都进入了这里面
if (!resolvedMethodParameter.hasParameterAnnotations()) {
return queryOrForm(parameterContext.getOperationContext());
}
该函数最后只会返回form和query两种类型
private static String queryOrForm(OperationContext context) {
if (context.consumes().contains(MediaType.APPLICATION_FORM_URLENCODED) && context.httpMethod() == HttpMethod
.POST) {
return "form";
}
return "query";
}
所以我们修改代码:
public static String findParameterType(ParameterContext parameterContext) {
...
} else if (!resolvedMethodParameter.hasParameterAnnotation(RequestBody.class) && httpMethod.matches("POST")) {
// 对Post方法并且没有@RequestBody描述的方法返回Body
return "body";
}
..
}
在其中添加一个else if 判断,对无RequestBody描述的参数也可以让它返回body
到这里我们就完成了对无@RequestBody注解的参数让其swagger也显示model模型
@RequestMapping描述方法时如何让swagger只展示post方法
许多使用@RequestMapping描述的方法在swagger中展示都会展示很多个方法,只是不同的请求类型。如何让他们只展示Post方法?
修改ApiOperationReader方法
原来的代码:
public List<Operation> read(RequestMappingContext outerContext) {
List<Operation> operations = newArrayList();
Set<RequestMethod> requestMethods = outerContext.getMethodsCondition();
Set<RequestMethod> supportedMethods = supportedMethods(requestMethods);
//Setup response message list
Integer currentCount = 0;
for (RequestMethod httpRequestMethod : supportedMethods) {
OperationContext operationContext = new OperationContext(new OperationBuilder(nameGenerator),
httpRequestMethod,
outerContext,
currentCount);
Operation operation = pluginsManager.operation(operationContext);
if (!operation.isHidden()) {
operations.add(operation);
currentCount++;
}
}
Collections.sort(operations, outerContext.operationOrdering());
return operations;
}
private Set<RequestMethod> supportedMethods(Set<RequestMethod> requestMethods) {
return requestMethods == null || requestMethods.isEmpty()
? allRequestMethods
: requestMethods;
}
注意
private Set<RequestMethod> supportedMethods(Set<RequestMethod> requestMethods) {
return requestMethods == null || requestMethods.isEmpty()
? allRequestMethods
: requestMethods;
}
该方法返回了allRequestMethods,这里面包括了
GET,
HEAD,
POST,
PUT,
PATCH,
DELETE,
OPTIONS,
TRACE;
这8种类型的Method,在上面循环的时候就会创建8个OperationContext,导致我们最终生成8个方法,找到该方法以后修改就比较简单了。
private Set<RequestMethod> supportedMethods(Set<RequestMethod> requestMethods) {
// 修改默认只显示Post方法
return requestMethods == null || requestMethods.isEmpty()
? Sets.newHashSet(RequestMethod.POST)
: requestMethods;
}
我们让它只返回Post方法。
我上面的内容是基于https://blog.csdn.net/Driver_tu/article/details/100031407该链接的扩展,具体怎么把上面的代码在项目中应用,可以查看该博客