swagger2使用小结

集成swagger2

1、引入依赖

<!-- swagger2 start-->
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <version>2.9.2</version>
</dependency>
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger-ui</artifactId>
    <version>2.9.2</version>
</dependency>
<!-- swagger2 end-->

2、新建swagger2配置类

类名没有要求,可以自己取。
@Configuration声明为配置类,@EnableSwagger2则开启Swagger2文档支持

package com.wla.code.groot.config.swagger2;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

/**
 * @Description 描述
 * @auther lei1.wang
 * @date 2020/05/09 10:23
 */
@Configuration
@EnableSwagger2
@Profile(value = {"dev","local"})
public class Swagger2Configuration {
    //api接口包扫描路径
    private static final String SWAGGER_SCAN_BASE_PACKAGE = "com.wla.code";

    private static final String VERSION = "1.0.0";

    @Bean
    public Docket createRestApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                //分组名,不指定默认为default
                .groupName("groot系统")
                //设置swagger-ui.html页面上的一些元素信息。
                .apiInfo(apiInfo())
                .select()
                //扫描的包路径
                .apis(RequestHandlerSelectors.basePackage(SWAGGER_SCAN_BASE_PACKAGE))
                // 定义要生成文档的Api的url路径规则
                .paths(PathSelectors.any())
                .build();
    }

    private ApiInfo apiInfo() {
        //Contact contact = new Contact("快乐男孩", "www.baidu.com", "my@my.com");
        return new ApiInfoBuilder()
                //设置文档的标题
                .title("Groot工程")
                // 设置文档的描述
                .description("Groot项目 API 接口文档")
                //服务条款
                //.termsOfServiceUrl("http://127.0.0.1:8080/")
                // 联系方式
                //.contact(contact)
                // 设置文档的版本信息
                .version(VERSION)
                .build();
    }
    
    /**
     * UI相关配置
     * 隐藏models
     */
    @Bean
    public UiConfiguration uiConfig() {
        return UiConfigurationBuilder.builder()
                .deepLinking(true)
                .displayOperationId(false)
                // 隐藏UI上的Models模块
                .defaultModelsExpandDepth(-1)
                .defaultModelExpandDepth(0)
                .defaultModelRendering(ModelRendering.EXAMPLE)
                .displayRequestDuration(false)
                .docExpansion(DocExpansion.NONE)
                .filter(false)
                .maxDisplayedTags(null)
                .operationsSorter(OperationsSorter.ALPHA)
                .showExtensions(false)
                .tagsSorter(TagsSorter.ALPHA)
                .validatorUrl(null)
                .build();
    }
}

3、访问地址

项目地址+/swagger-ui.html#
若ip是127.0.0.1,端口是8080,文档地址就是http://127.0.0.1:8080/swagger-ui.html#

相关注解

@Api()、@ApiOperation():标注controller和method

@Api()注解属性介绍(常用属性介绍)

属性备注
tags标记(controller注解)
protocolsPossible values: http, https, ws, wss.
producesFor example, “application/json, application/xml”
consumesFor example, “application/json, application/xml”
authorizations高级特性认证时配置

@ApiOperation()注解属性介绍(常用属性介绍)

属性备注
value方法后面的注释
notes对方法的详细描述
httpMethod“GET”, “HEAD”, “POST”, “PUT”, “DELETE”, “OPTIONS” and “PATCH”
codehttp的状态码 默认 200
hidden配置为true 将在文档中隐藏
extensions扩展属性

举例

@Api(tags = "用户模块")
@RestController
@RequestMapping("/user/")
public class UserController {

    @ApiOperation(value = "添加用户", httpMethod = "POST")
    @RequestMapping(value = "/add")
    public BaseRes<String> add(@RequestBody UserReq userReq) {
        //todo
        return new BaseRes<>();
    }
}

@ApiParam 用于方法、参数、字段上,请求属性

注解属性介绍(主要属性介绍)

属性备注
value注释
defaultValue默认值
required是否必填(加*号)
allowableValues允许的值
example示例
type传参类型例如:header

举例

@ApiParam(value = "用户登录Token", type = "header")

@ApiModel、@ApiModelProperty:用于JavaBean和JavaBean的属性,请求和返回实体

@ApiModel注解属性介绍(主要属性介绍)

属性备注
value注释
description详细描述

@ApiModelProperty注解属性介绍(主要属性介绍)

属性备注
value注释
description详细描述
value属性简要说明
allowableValues限制参数可接收的值,三种方法,固定取值,固定范围
access过滤属性,参阅:io.swagger.core.filter.SwaggerSpecFilter
notes注释会被value覆盖
required是否为必传参数,false:非必传参数; true:必传参数
position允许在模型中显示排序属性
hidden隐藏模型属性,false:不隐藏; true:隐藏
example属性的示例值
allowEmptyValue允许传空值,false:不允许传空值; true:允许传空值

举例

@Setter
@Getter
@ApiModel(value = "用户类请求体")
public class UserReq {
    @ApiModelProperty(notes = "用户名", required = true, example = "20200", position = 1)
    private String userName;
    @ApiModelProperty(notes = "真实名", required = true, example = "bob", position = 2)
    private String trueName;
    @ApiModelProperty(notes = "昵称", required = true, example = "bob", position = 3)
    private String nickname;
    @ApiModelProperty(notes = "手机号", required = true, example = "18011600000", position = 4)
    private String phone;
    @ApiModelProperty(notes = "邮箱", required = true, example = "31@qq.com", position = 5)
    private String email;
}

@ApiImplicitParams、@ApiImplicitParam:方法参数的说明

@ApiImplicitParams:用在请求的方法上,包含一组参数说明
    @ApiImplicitParam:对单个参数的说明      
        name:参数名
        value:参数的汉字说明、解释
        required:参数是否必须传
        paramType:参数放在哪个地方
            · header --> 请求参数的获取:@RequestHeader
            · query --> 请求参数的获取:@RequestParam
            · path(用于restful接口)--> 请求参数的获取:@PathVariable
            · body(请求体)-->  @RequestBody User user
            · form(普通表单提交)     
        dataType:参数类型,默认String,其它值dataType="int"       
        defaultValue:参数的默认值

举例:

@ApiOperation(value="用户登录",notes="随边说点啥")
@ApiImplicitParams({
        @ApiImplicitParam(name="mobile",value="手机号",required=true,paramType="form"),
        @ApiImplicitParam(name="password",value="密码",required=true,paramType="form"),
        @ApiImplicitParam(name="age",value="年龄",required=true,paramType="form",dataType="Integer")
})
@PostMapping("/login")
public AjaxResult login(@RequestParam String mobile, @RequestParam String password,
                        @RequestParam Integer age){
    //TODO
    return AjaxResult.OK();
}

单个参数举例

@ApiOperation("根据部门Id删除")
@ApiImplicitParam(name="depId",value="部门id",required=true,paramType="query")
@GetMapping("/delete")
public AjaxResult delete(String depId) {
    //TODO
}

@ApiResponses、@ApiResponse:方法返回值的说明

@ApiResponses:方法返回对象的说明
    @ApiResponse:每个参数的说明
        code:数字,例如400
        message:信息,例如"请求参数没填好"
        response:抛出异常的类

举例

@ApiOperation(value = "修改密码", notes = "方法的备注说明,如果有可以写在这里")
@ApiResponses({
        @ApiResponse(code = 400, message = "请求参数没填好"),
        @ApiResponse(code = 404, message = "请求路径找不到")
})
@PostMapping("/changepass")
public AjaxResult changePassword(@AutosetParam SessionInfo sessionInfo,
        @RequestBody @Valid PasswordModel passwordModel) {
    //TODO
}

@ApiIgnore 忽略属性或方法或者controller

此注解用于类,属性,方法上,用于隐藏某个controller或者方法或属性
Api中的hide属性失效,但是方法的hide属性能够生效,这里可直接用方法的hide属性


开发完成

屏蔽接口文档

在swagger2配置类上加上注解,指定显示文档的环境

@Profile("dev")
或
@Profile(value = {"dev","local"})

实战经验

在shiro配置类中放行swagger2相关资源

//swagger2免拦截
filterChainDefinitionMap.put("/swagger-ui.html**", "anon");
filterChainDefinitionMap.put("/v2/api-docs", "anon");
filterChainDefinitionMap.put("/swagger-resources/**", "anon");
filterChainDefinitionMap.put("/webjars/**", "anon");

一个请求实体多个接口使用时,隐藏掉不需要的属性

此方法是网上找到的一个方法。仅作笔记用

1. 自定义注解

package com.wla.code.manuf.config;

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

/**
 * @Description 忽略实体的某个或某几个属性。 swagger2 多个接口共用一个请求体时使用
 * @auther lei1.wang
 * @date 2020/11/03 10:41
 */
@Target({ElementType.PARAMETER, ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiIgp {
    String[] value(); //对象属性值
}

2. 实现parameterBuilderPlugin

ps: 注意这里用的是alibaba 的javassist 的包最好

package com.wla.colde.manuf.config;

import com.fasterxml.classmate.TypeResolver;
import com.google.common.base.Optional;
import io.swagger.annotations.ApiModelProperty;
import org.apache.ibatis.javassist.ClassPool;
import org.apache.ibatis.javassist.CtClass;
import org.apache.ibatis.javassist.CtField;
import org.apache.ibatis.javassist.NotFoundException;
import org.apache.ibatis.javassist.bytecode.AnnotationsAttribute;
import org.apache.ibatis.javassist.bytecode.ConstPool;
import org.apache.ibatis.javassist.bytecode.annotation.Annotation;
import org.apache.ibatis.javassist.bytecode.annotation.StringMemberValue;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
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 java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;

/**
 * @Description 描述
 * @auther lei1.wang
 * @date 2020/11/03 10:42
 */
@Component
@Order   //plugin加载顺序,默认是最后加载
public class MapApiReader implements ParameterBuilderPlugin {
    @Autowired
    private TypeResolver typeResolver;

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


        Optional<ApiIgp> optional = methodParameter.findAnnotation(ApiIgp.class);
        if (optional.isPresent()) {
            Random random = new Random();
            String name = originClass.getSimpleName() + "Model" + random.nextInt(100);  //model 名称
            String[] properties = optional.get().value();
            try {
                parameterContext.getDocumentationContext()
                        .getAdditionalModels()
                        .add(typeResolver.resolve(createRefModelIgp(properties, originClass.getPackage()+"."+name, originClass)));  //像documentContext的Models中添加我们新生成的Class
            } catch (Exception e) {
                e.printStackTrace();
            }
            parameterContext.parameterBuilder()  //修改Map参数的ModelRef为我们动态生成的class
                    .parameterType("body")
                    .modelRef(new ModelRef(name))
                    .name(name);
        }

    }

    private Class createRefModelIgp(String[] propertys, String name, Class origin) throws NotFoundException {
        ClassPool pool = ClassPool.getDefault();
        CtClass ctClass = pool.makeClass( name);
        try {
            Field[] fields = origin.getDeclaredFields();
            List<Field> fieldList = Arrays.asList(fields);
            List<String> ignorePropertys = Arrays.asList(propertys);
            List<Field> dealFileds = fieldList.stream().filter(s -> !ignorePropertys.contains(s.getName())).collect(Collectors.toList());
            for (Field field : dealFileds) {
                CtField ctField = new CtField(ClassPool.getDefault().get(field.getType().getName()), field.getName(), ctClass);
                ctField.setModifiers(Modifier.PUBLIC);
                ApiModelProperty ampAnno = origin.getDeclaredField(field.getName()).getAnnotation(ApiModelProperty.class);
                String attributes = java.util.Optional.ofNullable(ampAnno).map(s->s.value()).orElse("");
                if (StringUtils.isEmpty(attributes) ){ //添加model属性说明
                    ConstPool constPool = ctClass.getClassFile().getConstPool();
                    AnnotationsAttribute attr = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag);
                    Annotation ann = new Annotation(ApiModelProperty.class.getName(), constPool);
                    ann.addMemberValue("value", new StringMemberValue(attributes, constPool));
                    attr.addAnnotation(ann);
                    ctField.getFieldInfo().addAttribute(attr);
                }
                ctClass.addField(ctField);
            }
            return ctClass.toClass();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }




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

}

3. 使用注解

@ApiOperation(value = "保存", httpMethod = "POST")
@RequestMapping(value = "insert", method = RequestMethod.POST)
public String insert(@ApiIgp ({"id"}) @RequestBody EmployeeSaveReq req){
    return "success";
}

@ApiOperation(value = "修改", httpMethod = "POST")
@RequestMapping(value = "update", method = RequestMethod.POST)
public String update(@RequestBody EmployeeSaveReq req){
    return "success";
}

文献

github文档  


版权声明:本文为qq_32221913原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。