1、环境描述:在springboot+swagger v_2.9.2的环境下
前后端分离,restful风格接口。
2、提问原因:前后端约定使用form-data进行数据传递,后台接口入参很多都是:
public String findPage(String pageNo, String pageSize, Batch entity)这样的,
调试好swagger接口文档后发现:如果将参数Batch用@Requestbody修饰,接口文档中的参数parameter就是聚合显示,如果后台没有使用@Requestbody修饰,那么swagger将递归该参数对象Batch的所有属性,包括其中的Page,具体如下图:
(
图片描述:(不知道图片看的清不)
同一个接口,仅仅只是入参对象的修饰语不同,在swagger上居然区别这么大
左边的swagger上看起来是正常的,网络上很多人都是用这种@Requestbody方式,但是我们约定的是form-data,所以不能使用@Requestbody接收参数。而如果不用,那么swagger文档上看到的都是全部铺开的,不是很方便使用。
3、我尝试过的方法
1.我想过两个方向,一个是修改后台swagger处理数据的拼装逻辑,另一个方向是在页面端修改数据的位置和逻辑,也就是修改api-doc接口返回的数据,
==第一个方向:修改后台swagger处理数据的拼装结构,借鉴了(https://blog.csdn.net/u010579482/article/details/79990536) 中的一个思路,重写子类覆盖swagger主要处理参数数据的ModelAttributeParameterExpander,到目前为止还没有解决。
==第二个方向:修改页面端api-doc接口返回的数据,根据图上两种方式的对比,我们可以发现:
被@Requestbody修饰的入参对象,在swagger-ui的definitions中已经有了一个对象的定义或声明:
而没有被@Requestbody修饰的返回结果,在swagger-ui里返回结果就直接是在path.post.parameters中平铺在一起,也就是不方便所在,如果入参对象里有子对象,那递归出来就是一大片了。如下图:
4、现状:到目前为止,还没有解决这个问题,各位大佬有时间的话教育下小弟,小弟在线等着,还望大佬们不吝赐教,[感激][感激][感激]
明白你的意思了,按照你的意思做了一下,篇幅有限,只展示主要代码
自定义注解
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface FormData {
}
覆盖springfox.documentation.spring.web.readers.operation.OperationParameterReader
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class OperationParameterReaderSub extends OperationParameterReader {
private final ModelAttributeParameterExpander expander;
private final EnumTypeDeterminer enumTypeDeterminer;
@Autowired
private DocumentationPluginsManager pluginsManager;
@Autowired
public OperationParameterReaderSub(
ModelAttributeParameterExpander expander,
EnumTypeDeterminer enumTypeDeterminer,
ApplicationContext applicationContext) {
super(expander, enumTypeDeterminer);
this.expander = expander;
this.enumTypeDeterminer = enumTypeDeterminer;
/*
* 删除父类bean定义
* DocumentationPluginsManager中使用ParameterBuilderPlugin类型获取bean,而不是OperationParameterReader类型
* 所以@Primary注解并不能覆盖父类
*/
DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) applicationContext.getAutowireCapableBeanFactory();
defaultListableBeanFactory.removeBeanDefinition("operationParameterReader");
}
private boolean shouldExpand(final ResolvedMethodParameter parameter, ResolvedType resolvedParamType) {
return !parameter.hasParameterAnnotation(RequestBody.class)
&& !parameter.hasParameterAnnotation(FormData.class) // 加上自定义的注解
&& !parameter.hasParameterAnnotation(RequestPart.class)
&& !parameter.hasParameterAnnotation(RequestParam.class)
&& !parameter.hasParameterAnnotation(PathVariable.class)
&& !isBaseType(typeNameFor(resolvedParamType.getErasedType()))
&& !enumTypeDeterminer.isEnum(resolvedParamType.getErasedType())
&& !isContainerType(resolvedParamType)
&& !isMapType(resolvedParamType);
}
// 其他省略,直接从父类复制
}
覆盖springfox.documentation.spring.web.readers.operation.OperationModelsProvider
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class OperationModelsProviderSub extends OperationModelsProvider {
private static final Logger LOG = LoggerFactory.getLogger(OperationModelsProvider.class);
private final TypeResolver typeResolver;
@Autowired
public OperationModelsProviderSub(TypeResolver typeResolver, ApplicationContext applicationContext) {
super(typeResolver);
this.typeResolver = typeResolver;
/*
* 删除父类bean定义
* DocumentationPluginsManager中使用OperationModelsProviderPlugin类型获取bean,而不是OperationModelsProvider类型
* 所以@Primary注解并不能覆盖父类
*/
DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) applicationContext.getAutowireCapableBeanFactory();
defaultListableBeanFactory.removeBeanDefinition("operationModelsProvider");
}
private void collectParameters(RequestMappingContext context) {
LOG.debug("Reading parameters models for handlerMethod |{}|", context.getName());
List<ResolvedMethodParameter> parameterTypes = context.getParameters();
for (ResolvedMethodParameter parameterType : parameterTypes) {
if (parameterType.hasParameterAnnotation(RequestBody.class)
|| parameterType.hasParameterAnnotation(FormData.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);
}
}
LOG.debug("Finished reading parameters models for handlerMethod |{}|", context.getName());
}
// 其他省略,直接从父类复制
}
示例controller
像使用RequestBody一样使用FromData,只变文档,没有其他副作用
@GetMapping
@ApiOperation(value = "测试")
public String test(@FormData User user) {
System.out.println(user);
return "随便返回点什么";
}
既然想要自己组装的返回格式又不想用@RequestBody,那就自己在后台写个json的组装工具然后通过response.getWriter().write(json);写出去不就行了
我使用的是springfox-swagger2,2.2.2版本
可以实现Swagger官方提供的插件:https://springfox.github.io/springfox/docs/snapshot/#plugins
我尝试了实现OperationBuilderPlugin,ParameterBuilderPlugin,OperationModelsProviderPlugin并在其中添加自定义的注解;
最后只留下了实现OperationModelsProviderPlugin的方法,这样可以展开DTO里的参数,和RequestBody效果相同;
不过我没有深入去了解这些,功能实现了就OK了,有空写一篇博客记录一下
请问解决了吗?遇到了相同的问题
大佬我也碰到这样的问题,请问有解决吗?
不用@Requestbody,用@ModelAttribute可以么
我们公司是这么处理的,新建一个param包,专门用来存放接收参数的实体类,然后用@ApiModel,@ApiModelProperty加文档注释,不知道这样不用@RequestBody是什么样,没试过