swagger配置
pom.xml
<properties>
<swagger.version>2.7.0</swagger.version>
<swagger.bootstrap.version>1.9.5</swagger.bootstrap.version>
</properties>
<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>
配置
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());
}
// 如果有多个,可直接向下写
}
访问项目地址
http://localhost:8086/aj/doc.html
处理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();
}
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;// 是否必传
}
切面设置
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;
}
}
使用示例
/**
* {@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);
}
说明:
@ApiOperationSupport :属性responses:用于返回的参数说明
@DynamicResponseParameters:注解中name属性相当于给返回的实体的名字,如果重名的话,是会覆盖的
@ApiJsonObject:本人自定义的注解 name也是实体属性的名字,必须唯一。
此切面的作用是在项目启动的时候,根据方法上的注解,在运行时动态生成对象。
效果展示
图一是@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();
}
文件下载
@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);
}
不过,本人按照官网说明使用中发现其支持下载的内容是有限的。
本人上传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 {
//.....
}
最后附上swagger-ui 官网