当前位置:   article > 正文

swagger:处理Map类型的参数,文件上传以及下载_swaager map

swaager map

swagger配置

pom.xml
<properties>
	<swagger.version>2.7.0</swagger.version>
	<swagger.bootstrap.version>1.9.5</swagger.bootstrap.version>
</properties>
  • 1
  • 2
  • 3
  • 4
<dependency>
	<groupId>io.springfox</groupId>
	<artifactId>springfox-swagger2</artifactId>
	<version>${swagger.version}</version>
</dependency>
<dependency>
	<groupId>com.github.xiaoymin</groupId>
	<artifactId>swagger-bootstrap-ui</artifactId>
	<version>${swagger.bootstrap.version}</version>
</dependency>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
配置
package net.sinorock.aj.common.config;


import com.github.xiaoymin.swaggerbootstrapui.annotations.EnableSwaggerBootstrapUI;
import io.swagger.annotations.ApiOperation;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.ApiKey;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

import java.util.List;

import static com.google.common.collect.Lists.newArrayList;


@Configuration
@EnableSwagger2
//EnableSwaggerBootstrapUI 开启增强功能,比如文件上传和下载功能~可不开启
@EnableSwaggerBootstrapUI  
public class SwaggerConfig extends WebMvcConfigurerAdapter
{

    @Bean
    public Docket createBaseRestApi()
    {
        ApiInfo apiInfo = new ApiInfoBuilder().title("行政执法系统-base").description(
            "API接口文档公布").termsOfServiceUrl("").version("0.1").build();
        return new Docket(DocumentationType.SWAGGER_2).groupName("APP接口-base").apiInfo(
            apiInfo).select()
            // 加了ApiOperation注解的类,才生成接口文档
         .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
            // 包下的类,才生成接口文档
            .apis(RequestHandlerSelectors.basePackage("net.sinorock.aj.modules.base")).paths(
                PathSelectors.any()).build().securitySchemes(security());
    }

	// 如果有多个,可直接向下写
}
  • 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
访问项目地址
  http://localhost:8086/aj/doc.html
  • 1

处理Map参数

自定义注解
package net.sinorock.aj.common.annotation;


import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


/**
 * {@code ApiJsonObject} ApiJsonObject 实体对象
 * @author nov
 */
@Target({ElementType.PARAMETER, ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiJsonObject {
    ApiJsonProperty[] value();
    String name();
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
package net.sinorock.aj.common.annotation;


import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


/**
 * {@code ApiJsonProperty} ApiJsonProperty 实体属性
 * @author nov
 */
@Target({ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiJsonProperty {

    String key(); // key

    String example() default "";// 示例

    String type() default "string";// 支持string、int、double

    String description() default ""; 参数描述

    boolean required() default true;// 是否必传
}

  • 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
切面设置
package net.sinorock.aj.common.config;


import com.fasterxml.classmate.TypeResolver;
import com.google.common.base.Optional;
import javassist.*;
import javassist.bytecode.AnnotationsAttribute;
import javassist.bytecode.ConstPool;
import javassist.bytecode.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ResolvedMethodParameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.ParameterBuilderPlugin;
import springfox.documentation.spi.service.contexts.ParameterContext;
import net.sinorock.aj.common.annotation.ApiJsonObject;
import net.sinorock.aj.common.annotation.ApiJsonProperty;

import java.util.Map;


/**
 * {@code MapReaderForApi} 将map入参匹配到swagger文档的工具类
 * @author nov
 */
@Component
@Order
public class MapReaderForApi implements ParameterBuilderPlugin
{

    /**
     * 类型处理器
     */
    @SuppressWarnings("SpringJavaAutowiredFieldsWarningInspection")
    @Autowired
    private TypeResolver typeResolver;

    /**
     * 动态生成的虚拟DTO Class的包路径
     */
    private final static String BASEPACKAGE = "net.sinorock.aj.modules.dto.";

    @Override
    public void apply(ParameterContext parameterContext)
    {
        ResolvedMethodParameter methodParameter = parameterContext.resolvedMethodParameter();

        // 判断是否需要修改对象ModelRef,这里我判断的是Map类型和String类型需要重新修改ModelRef对象
        if (methodParameter.getParameterType().canCreateSubtype(Map.class)
            || methodParameter.getParameterType().canCreateSubtype(String.class))
        {
            // 根据参数上的ApiJsonObject注解中的参数动态生成Class
            @SuppressWarnings("Guava")
            Optional<ApiJsonObject> optional = methodParameter.findAnnotation(ApiJsonObject.class);
            if (optional.isPresent())
            {
                // model 名称
                String name = optional.get().name();
                ApiJsonProperty[] properties = optional.get().value();

                // 向documentContext的Models中添加我们新生成的Class
                parameterContext.getDocumentationContext().getAdditionalModels().add(
                    typeResolver.resolve(createRefModel(properties, name)));

                // 修改Map参数的ModelRef为我们动态生成的class
                parameterContext.parameterBuilder().parameterType("body").modelRef(
                    new ModelRef(name)).name(name);
            }
        }
    }

    /**
     * {@code createRefModel} 
     * <p>根据propertys中的值动态生成含有Swagger注解的javaBeen</p>
     * @author nov
     * @param propertys 属性
     * @param name 名字
     * @return java.lang.Class
     */
    @SuppressWarnings("rawtypes")
    private Class createRefModel(ApiJsonProperty[] propertys, String name)
    {
        ClassPool pool = ClassPool.getDefault();
        CtClass ctClass = pool.makeClass(BASEPACKAGE + name);

        try
        {
            for (ApiJsonProperty property : propertys)
            {
                ctClass.addField(createField(property, ctClass));
            }
            return ctClass.toClass();
        }
        catch (Exception e)
        {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * {@code createField} 
     * <p>根据property的值生成含有swagger apiModelProperty注解的属性</p>
     * @author nov
     * @param property 属性
     * @param ctClass 类
     * @return javassist.CtField
     * @throws NotFoundException 异常
     * @throws CannotCompileException 异常
     */
    private CtField createField(ApiJsonProperty property, CtClass ctClass)
        throws NotFoundException,
        CannotCompileException
    {
        CtField ctField = new CtField(getFieldType(property.type()), property.key(), ctClass);
        ctField.setModifiers(Modifier.PUBLIC);

        ConstPool constPool = ctClass.getClassFile().getConstPool();

        AnnotationsAttribute attr = new AnnotationsAttribute(constPool,
            AnnotationsAttribute.visibleTag);
        Annotation ann = new Annotation("io.swagger.annotations.ApiModelProperty", constPool);
        ann.addMemberValue("value", new StringMemberValue(property.description(), constPool));
        // string类型
        if (ctField.getType().subclassOf(ClassPool.getDefault().get(String.class.getName())))
        {
            ann.addMemberValue("example", new StringMemberValue(property.example(), constPool));
        }

        // int类型
        if (ctField.getType().subclassOf(ClassPool.getDefault().get(Integer.class.getName())))
        {
            ann.addMemberValue("example",
                new IntegerMemberValue(Integer.parseInt(property.example()), constPool));
        }

        // double类型
        if (ctField.getType().subclassOf(ClassPool.getDefault().get(Double.class.getName())))
        {
            ann.addMemberValue("example",
                new DoubleMemberValue(Double.parseDouble(property.example()), constPool));
        }
        ann.addMemberValue("required", new BooleanMemberValue(property.required(), constPool));

        attr.addAnnotation(ann);
        ctField.getFieldInfo().addAttribute(attr);

        return ctField;
    }

    /**
     * {@code getFieldType} 
     * @author nov
     * @param type 类型
     * @return javassist.CtClass
     * @throws NotFoundException 异常
     */
    private CtClass getFieldType(String type)
        throws NotFoundException
    {
        CtClass fileType = null;
        switch (type)
        {
            case "string":
                fileType = ClassPool.getDefault().get(String.class.getName());
                break;
            case "int":
                fileType = ClassPool.getDefault().get(Integer.class.getName());
                break;
            case "double":
                fileType = ClassPool.getDefault().get(Double.class.getName());
                break;
            default:
                break;
        }
        return fileType;
    }

    @Override
    public boolean supports(DocumentationType delimiter)
    {
        return true;
    }
}

  • 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
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
使用示例
/**
     * {@code list} 系统用户列表
     * @author nov
     * @param params 参数
     * @return net.sinorock.aj.common.web.def.R
     */
    @PostMapping("/list")
    @ApiOperation("系统用户列表")
    @ApiOperationSupport(responses = @DynamicResponseParameters(name = "userRD", properties = {
            @DynamicParameter(value = "状态码", name = "code"),
            @DynamicParameter(value = "消息", name = "msg"),
            @DynamicParameter(value = "数据", name = "page.list", dataTypeClass = SysUserEntity.class),
            @DynamicParameter(value = "数据", name = "page")}))
    public R list(@ApiJsonObject(name = "userRequestModel", value = {
            @ApiJsonProperty(key = "isPaging", example = "false", description = "是否分页", required = false),
            @ApiJsonProperty(key = "page", example = "1", description = "页数,不传默认是1", required = false),
            @ApiJsonProperty(key = "limit", example = "10", description = "页大小,不传默认是10", required = false),
            @ApiJsonProperty(key = "username", example = "史祖平", description = "用户姓名", required = false),
            @ApiJsonProperty(key = "cardId", example = "JSZF02090057", description = "证件号", required = false),
            @ApiJsonProperty(key = "deptCode", example = "3202", description = "部门id", required = false),
            @ApiJsonProperty(key = "status", example = "1", description = "状态  0:禁用   1:正常", required = false)}) @RequestBody Map<String, Object> params) {
        PageUtils page = sysUserService.queryPage(params);
        return R.ok().page(page);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

说明:
@ApiOperationSupport :属性responses:用于返回的参数说明
@DynamicResponseParameters:注解中name属性相当于给返回的实体的名字,如果重名的话,是会覆盖的
@ApiJsonObject:本人自定义的注解 name也是实体属性的名字,必须唯一。

此切面的作用是在项目启动的时候,根据方法上的注解,在运行时动态生成对象。

效果展示

图一是@ApiJsonObject的作用
图二是@ApiOperationSupport 中responses属性的作用
图三是@ApiOperationSupport中生成实体的位置,可在此查看
图一 @ApiJsonObject的作用
图二是@ApiOperationSupport 中responses属性的作用

@ApiOperationSupport中生成实体的位置

文件上传

@ApiImplicitParams 是swagger的参数说明

	@PostMapping("/upload")
    @ApiImplicitParams({
        @ApiImplicitParam(name = "file", value = "文件流对象", required = true, dataType = "__File", paramType = "form"),
        @ApiImplicitParam(name = "sourceId", value = "来源id,不做处理", paramType = "query")})
    @ApiOperation("附件上传")
    @ApiOperationSupport(responses = @DynamicResponseParameters(name = "accseeoryRUpload", properties = {
        @DynamicParameter(value = "状态码", name = "code"),
        @DynamicParameter(value = "消息", name = "msg"),
        @DynamicParameter(value = "附件路径", name = "path"),
        @DynamicParameter(value = "附件id", name = "accessoryId"),
        @DynamicParameter(value = "来源id", name = "sourceId")}))
    R upload(MultipartFile file,@RequestParam(required = false) String sourceId)
        throws IOException
    {
        String userId = ShiroUtils.getUserId();
        AccessoryEntity accessoryEntity = accessoryService.upload(file, userId);
        return R.ok();
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

在这里插入图片描述
在这里插入图片描述

文件下载

@GetMapping(value = "/download", produces = "application/octet-stream")
    @ApiOperation("附件下载")
    @ApiImplicitParams({
        @ApiImplicitParam(name = "accessoryId", value = "附件id", required = true, paramType = "query")})
    void download(@RequestParam("accessoryId") String accessoryId, HttpServletResponse response)
        throws IOException
    {
        accessoryService.downLoad(accessoryId, response);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

在这里插入图片描述
不过,本人按照官网说明使用中发现其支持下载的内容是有限的。
本人上传pdf,二进制流返回下载之后是txt…
在这里插入图片描述

参数忽略:ignoreParameters

ApiOperationSupport 的 ignoreParameters :有时候某个实体的作为请求实体时,有字段是多余了。为方便前端对接,可以使用ignoreParameters 进行忽略,这样swagger的请求示例会自动忽略。方便前端~

 	@PostMapping("/handleFzysAudit")
    @ApiOperation("法规预审")
    @ApiOperationSupport(ignoreParameters = {"auditBo.submitterUserName", "auditBo.submitDeptName",
            "auditBo.submitTime", "auditBo.handlerUserName", "auditBo.handlerDeptName",
            "auditBo.handlerTime", "auditBo.msgContent", "auditBo.submitterUserId",
            "auditBo.submitDeptId", "auditBo.submitTime", "auditBo.title",
            "auditBo.auditResult"}, responses = @DynamicResponseParameters(name = "auditR", properties = {
            @DynamicParameter(value = "状态码", name = "code"),
            @DynamicParameter(value = "消息", name = "msg")}))
    public R handleFzysAudit(@RequestBody AuditFzshBo auditBo) throws OperaException, IllegalAccessException, InstantiationException {
        //.....
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

最后附上swagger-ui 官网

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/正经夜光杯/article/detail/897725
推荐阅读
相关标签
  

闽ICP备14008679号