spring boot 整合swagger

spring boot 整合swagger

相信写接口文档是我们程序员最烦躁的一个环节,不知道大家是不是这样感觉的,不过俺是这样感觉的。所以想到了swagger来自动生成接口文档。特在此记录一下,有哪点不好的还请各位大佬勿喷。多多指教。话不多说。。。。。

首先我们的添加swagger所需要的jar包

		<dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.7.0</version>
        </dependency>

        <dependency>
            <groupId>org.javassist</groupId>
            <artifactId>javassist</artifactId>
            <version>3.23.1-GA</version>
        </dependency>


        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>25.1-jre</version>
        </dependency>

        <!-- swagger-ui -->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.5.0</version>
        </dependency>

        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>swagger-bootstrap-ui</artifactId>
            <version>1.8.1</version>
        </dependency>

下一步,创建SwaggerConfig配置

package com.shili.shwx.config.swagger;

import com.shili.shwx.config.FixedValueConfig;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.service.Parameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

import java.util.ArrayList;

/**
 * @author chy
 * @version 1.0
 * @since 2020/2/14
 * 配置swgger自动生成接口文档
 */
@Configuration
@EnableSwagger2
public class SwggerConfig {
    @Value("${base.package:xxxx.xxx.xxx.controller}")      //指定所需生成的接口路径(一般指定到controller路径下)
    public String rootPackage;

    @Bean
    public Docket productApi() {
        //**********************添加请求头参数(不需要添加请求头参数的请忽略)******************************
        ParameterBuilder parameterBuilder = new ParameterBuilder();
        ParameterBuilder parameterBuilder1 = new ParameterBuilder();
        ParameterBuilder parameterBuilder2 = new ParameterBuilder();
        ParameterBuilder parameterBuilder3 = new ParameterBuilder();
        ArrayList<Parameter> parameters = new ArrayList<>();
        parameterBuilder.name("").description("").modelRef(new ModelRef("")).parameterType("").required(true);
        parameterBuilder1.name("platform").description("").modelRef(new ModelRef("Int")).parameterType("header").required(true);
        parameterBuilder2.name("").description("").modelRef(new ModelRef("String")).parameterType("header").required(true);
        parameterBuilder3.name("").description("").modelRef(new ModelRef("String")).parameterType("header").required(false);
        parameters.add(parameterBuilder.build());
        parameters.add(parameterBuilder1.build());
        parameters.add(parameterBuilder2.build());
        parameters.add(parameterBuilder3.build());
        //******************************************请求头添加结束*************************************

        return new Docket(DocumentationType.SWAGGER_2)
                .select()
                .apis(RequestHandlerSelectors.basePackage(rootPackage))
                .build()
                .globalOperationParameters(parameters)
                .apiInfo(metaData());
    }

    private ApiInfo metaData() {
        return new ApiInfoBuilder()
                .title("微信小程序chy的接口文档")
                .description("")
                .version(FixedValueConfig.sysVersion)
                .contact(new Contact("邮箱地址","blog.csdn.net","2645740054@qq.com"))
                .license("My interface document")
                .licenseUrl("http://localhost:0000/xxxx/swagger-ui.html#/")
                .build();
    }
}

到此swagger配置结束,在浏览器中

http://localhost:端口号/项目名/doc.html

打开这个路径就可以访问自动生成好的接口文档了,如下面展示:
在这里插入图片描述

注意有拦截器的请放行swagger相关的路径

		//放行Swagger2页面
        filterChainDefinitionMap.put("/swagger-ui.html","anon");
        filterChainDefinitionMap.put("/swagger/**","anon");
        filterChainDefinitionMap.put("/webjars/**", "anon");
        filterChainDefinitionMap.put("/swagger-resources/**","anon");
        filterChainDefinitionMap.put("/v2/**","anon");

还是比较简单的,不过有一点就是请求响应的返回格式是Map结构或者json格式的就不能详细的注解map结构或json格式里的字段,这点就很无奈了,当然也有相应的解决办法,解决的思路就是我们手动去告诉swagger返回的格式中有哪些字段,这些字段的解释是什么。如下就可以解决问题:

首选得自定义两个注解

/**
 * @author chy
 * @ClassName: ApiReturnJson
 * @Description: 返回对象的定义 (描述这个类的作用)
 * @version 1.0
 * @since 2020/3/18
 */
@Target({ElementType.PARAMETER, ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiReturnJson {
    ApiReturnJsonPro[] value(); //对象属性值

    String name();  //对象名称

}

/**
 * @author chy
 * @ClassName: ApiReturnJsonPro
 * @Description: 每一个字段的定义备注说明 (描述这个类的作用)
 * @version 1.0
 * @since 2020/3/18
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiReturnJsonPro {
    String key();  //key

    String example() default "";

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

    String description() default "";
}

然后定义一个类继承 OperationModelsProviderPlugin

package com.shili.shwx.config.swagger;

import com.fasterxml.classmate.ResolvedType;
import com.fasterxml.classmate.TypeResolver;
import com.google.common.base.Optional;
import com.shili.shwx.annotation.ApiReturnJson;
import com.shili.shwx.annotation.ApiReturnJsonPro;
import com.shili.shwx.entity.ResultEntity;
import javassist.*;
import javassist.bytecode.AnnotationsAttribute;
import javassist.bytecode.ConstPool;
import javassist.bytecode.annotation.Annotation;
import javassist.bytecode.annotation.IntegerMemberValue;
import javassist.bytecode.annotation.StringMemberValue;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.OperationModelsProviderPlugin;
import springfox.documentation.spi.service.contexts.RequestMappingContext;

/**
 * @author chy
 * @version 1.0
 * @since 2020/3/18
 */
@Component
@Order   //plugin加载顺序,默认是最后加载
public class SwggerResult implements OperationModelsProviderPlugin {
    @Autowired
    private TypeResolver typeResolver;

    private final static String basePackage = "com.xx.xx.xx.swagger.dto.";  //动态生成的Class名(这个路径是你想要的存放生成好的虚拟dto)

    @Override
    public void apply(RequestMappingContext context) {
        if (context.getReturnType().isInstanceOf(ResultEntity.class)) {
            // 根据参数上的ApiJsonObject注解中的参数动态生成Class
            Optional<ApiReturnJson> optional = context.findAnnotation(ApiReturnJson.class);
            if(optional.isPresent()){
                ApiReturnJson apiReturnJson = optional.get();
                String name = apiReturnJson.name()+"_"+context.getName();
                ApiReturnJsonPro[] properties  = optional.get().value();
                ResolvedType rt = typeResolver.resolve(createRefModel(properties, name));
                // 像documentContext的Models中添加我们新生成的Class
                context.getDocumentationContext().getAdditionalModels().add(rt);
                context.operationModelsBuilder().addReturn(rt).build();
            }
        }

    }


    /**
     * 根据propertys中的值动态生成含有Swagger注解的javaBeen
     */
    private Class createRefModel(ApiReturnJsonPro[] propertys, String name) {
        ClassPool pool = ClassPool.getDefault();
        CtClass ctClass = pool.makeClass(basePackage + name);

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



    /**
     * 根据property的值生成含有swagger apiModelProperty注解的属性
     */
    private CtField createField(ApiReturnJsonPro 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));
        if (ctField.getType().subclassOf(ClassPool.getDefault().get(String.class.getName())))
            ann.addMemberValue("example", new StringMemberValue(property.example(), constPool));
        if (ctField.getType().subclassOf(ClassPool.getDefault().get(Integer.class.getName())))
            ann.addMemberValue("example", new IntegerMemberValue(Integer.parseInt(property.example()), constPool));

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

        return ctField;
    }

    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;
        }
        return fileType;
    }

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

在定义一个类 OperationBuilderPlugin

package com.shili.shwx.config.swagger;

import com.google.common.base.Optional;
import com.shili.shwx.annotation.ApiReturnJson;
import com.shili.shwx.entity.ResultEntity;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ResponseMessage;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.OperationBuilderPlugin;
import springfox.documentation.spi.service.contexts.OperationContext;

import java.util.HashSet;
import java.util.Set;

/**
 * @author chy
 * @version 1.0
 * @since 2020/3/18
 */
@Component
@Order   //plugin加载顺序,默认是最后加载
public class SwggerOperationBuilderPlugin implements OperationBuilderPlugin {
    @Override
    public void apply(OperationContext operationContext) {
        // TODO Auto-generated method stub
        if(operationContext.getReturnType().isInstanceOf(ResultEntity.class)) {
            //根据参数上的ApiJsonObject注解中的参数动态生成Class
            Optional<ApiReturnJson> optional = operationContext.findAnnotation(ApiReturnJson.class);
            if(optional.isPresent()){
                ApiReturnJson apiReturnJson = optional.get();
                String name = apiReturnJson.name()+"_"+operationContext.getName();
                Set<ResponseMessage> set = new HashSet<ResponseMessage>();
                ModelRef mr = new ModelRef(name);
                set.add(new ResponseMessage(200,"返回json用例说明",mr,null,null));
                operationContext.operationBuilder().responseMessages(set);
            }
        }
    }

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

最后就可以再接口中引用就完成了,接口引用实例:

	/**
     * 测试
     * @return
     */
    @ApiOperation(value = "测试一")
    @RequestMapping(value = "test1",method = {RequestMethod.GET,RequestMethod.POST})
    @ApiImplicitParams({
            @ApiImplicitParam(name = "testId", value = "测试Id",dataType = "int", paramType = "query",required = true)
    })
    @ApiReturnJson(name = "PartriachCurriculaDetail",value = {
            @ApiReturnJsonPro(key = "testName",example = "测试成功",description = "返回json格式中的字段testName的解释说明")
    })
    public ResultEntity partriachCurriculaDetail(Integer testId){
  		 return new ResultEntity('n',"10002",{"testName":"测试成功"}",null,null);
    }

到此整个spring boot 和swagger的整合完成,有不足的地方还请多多指教

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值