Driver_tu
Driver_tu
采纳率100%
2019-08-20 21:43

springboot中form-data传值,不用@Requestbody修饰入参对象时,swagger-ui该怎么聚合它的属性

20
已采纳

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-ui的影响

图片描述:(不知道图片看的清不)

同一个接口,仅仅只是入参对象的修饰语不同,在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修饰
图片说明

而没有被@Requestbody修饰的返回结果,在swagger-ui里返回结果就直接是在path.post.parameters中平铺在一起,也就是不方便所在,如果入参对象里有子对象,那递归出来就是一大片了。如下图:

图片说明

4、现状:到目前为止,还没有解决这个问题,各位大佬有时间的话教育下小弟,小弟在线等着,还望大佬们不吝赐教,[感激][感激][感激]

  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 复制链接分享
  • 邀请回答

5条回答

  • qq_35433926 薛凌康 2年前

    明白你的意思了,按照你的意思做了一下,篇幅有限,只展示主要代码

    自定义注解

    @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 "随便返回点什么";
        }
    
    点赞 1 评论 复制链接分享
  • weixin_40330008 许水愿 2年前

    我使用的是springfox-swagger2,2.2.2版本
    可以实现Swagger官方提供的插件:https://springfox.github.io/springfox/docs/snapshot/#plugins
    我尝试了实现OperationBuilderPlugin,ParameterBuilderPlugin,OperationModelsProviderPlugin并在其中添加自定义的注解;
    最后只留下了实现OperationModelsProviderPlugin的方法,这样可以展开DTO里的参数,和RequestBody效果相同;
    不过我没有深入去了解这些,功能实现了就OK了,有空写一篇博客记录一下

    点赞 1 评论 复制链接分享
  • BestwishesTolover BestwishesTolover 2年前

    既然想要自己组装的返回格式又不想用@RequestBody,那就自己在后台写个json的组装工具然后通过response.getWriter().write(json);写出去不就行了

    点赞 评论 复制链接分享
  • qq_37243472 热爱编程,热爱生活 2年前

    我们公司是这么处理的,新建一个param包,专门用来存放接收参数的实体类,然后用@ApiModel,@ApiModelProperty加文档注释,不知道这样不用@RequestBody是什么样,没试过

    点赞 评论 复制链接分享
  • weixin_43082585 weixin_43082585 2年前

    不用@Requestbody,用@ModelAttribute可以么

    点赞 评论 复制链接分享