赞
踩
<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
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…
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 官网
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。