集成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注解) |
protocols | Possible values: http, https, ws, wss. |
produces | For example, “application/json, application/xml” |
consumes | For example, “application/json, application/xml” |
authorizations | 高级特性认证时配置 |
@ApiOperation()注解属性介绍(常用属性介绍)
属性 | 备注 |
---|---|
value | 方法后面的注释 |
notes | 对方法的详细描述 |
httpMethod | “GET”, “HEAD”, “POST”, “PUT”, “DELETE”, “OPTIONS” and “PATCH” |
code | http的状态码 默认 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";
}
文献
版权声明:本文为qq_32221913原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。