赞
踩
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>swagger-bootstrap-ui</artifactId>
<version>1.9.2</version>
</dependency>
package com.sean.demo04.config; import com.github.xiaoymin.swaggerbootstrapui.annotations.EnableSwaggerBootstrapUI; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import springfox.documentation.builders.ApiInfoBuilder; 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; @Configuration //必须存在 @EnableSwagger2 //必须存在 @EnableSwaggerBootstrapUI //第三方swagger增强API注解 public class SwaggerConfig { @Bean public Docket customDocket() { return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()); } /* @Bean public Docket customDocket(){ return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .groupName("学生管理") .select() .apis(RequestHandlerSelectors.basePackage("com.xiaomin.controller")) .paths(PathSelectors.any()) .build(); }*/ /** * name:开发者姓名 * url:开发者网址 * email:开发者邮箱 * @return */ private ApiInfo apiInfo() { Contact contact = new Contact("杨震", "http://www.yangzhen.com", "153****8032@163.com"); return new ApiInfoBuilder() .title("杨震测试API接口")//标题 .description("API接口的描述")//文档接口的描述 .contact(contact) .version("1.0.0")//版本号 .build(); } }
通过访问即可 http://localhost:[port]/doc.html
主类
package com.sean.demo04.plugin; import com.alibaba.fastjson.JSONObject; import com.fasterxml.classmate.TypeResolver; import com.google.common.base.Optional; import javassist.*; import javassist.CannotCompileException; import javassist.ClassPool; import javassist.CtClass; import javassist.CtField; import javassist.bytecode.AnnotationsAttribute; import javassist.bytecode.ConstPool; import javassist.bytecode.annotation.Annotation; import javassist.bytecode.annotation.BooleanMemberValue; 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.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.util.Map; @Component @Order //plugin加载顺序,默认是最后加载 public class MapApiReader implements ParameterBuilderPlugin { @Autowired private TypeResolver typeResolver; @Override public void apply(ParameterContext parameterContext) { ResolvedMethodParameter methodParameter = parameterContext.resolvedMethodParameter(); if (methodParameter.getParameterType().canCreateSubtype(JSONObject.class) ||methodParameter.getParameterType().canCreateSubtype(Map.class) || methodParameter.getParameterType().canCreateSubtype(String.class)) { //判断是否需要修改对象ModelRef,这里我判断的是Map类型和String类型需要重新修改ModelRef对象 Optional<ApiJsonObject> optional = methodParameter.findAnnotation(ApiJsonObject.class); //根据参数上的ApiJsonObject注解中的参数动态生成Class if (optional.isPresent()) { String name = optional.get().name(); //model 名称 String paramType = optional.get().paramType(); //请求类型 ApiJsonProperty[] properties = optional.get().value(); parameterContext.getDocumentationContext().getAdditionalModels().add(typeResolver.resolve(createRefModel(properties, name))); //像documentContext的Models中添加我们新生成的Class parameterContext.parameterBuilder() //修改Map参数的ModelRef为我们动态生成的class .parameterType(paramType) .modelRef(new ModelRef(name)).description("对象参数") .name(name); } } } private final static String basePackage = "com.sean.demo04.swagger.model."; //动态生成的Class名 /** * 根据propertys中的值动态生成含有Swagger注解的javaBeen */ 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; } } /** * 根据property的值生成含有swagger apiModelProperty注解的属性 */ 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)); ann.addMemberValue("type", new StringMemberValue(property.type(), constPool)); ann.addMemberValue("paramType", new StringMemberValue(property.paramType(), constPool)); ann.addMemberValue("required", new BooleanMemberValue(property.required(), 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(int.class.getName()))) ann.addMemberValue("example", new IntegerMemberValue(constPool,Integer.parseInt(property.example()))); // 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(int.class.getName()); break; } return fileType; } @Override public boolean supports(DocumentationType documentationType) { return true; } }
两个接口
package com.sean.demo04.plugin; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.ANNOTATION_TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface ApiJsonProperty { String key(); //key String example() default ""; String type() default "String"; //支持string 和 int String description() default ""; String paramType() default ""; boolean required() default true; }
package com.sean.demo04.plugin; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.PARAMETER, ElementType.FIELD, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface ApiJsonObject { ApiJsonProperty[] value(); //对象属性值 String name(); //对象名称 String paramType();//请求类型 }
返回实体必须加泛型
package com.sean.demo04.utils; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; /************************************************************* * Description: 统一返回结果类 * Author: zhang shanming * CreateTime: 2019/1/7 ************************************************************/ @ApiModel public class TelResult<T>{ // 状态码:1成功,其他为失败 @ApiModelProperty(value = " 状态码:1成功,0为失败") public int code; // 成功为success,其他为失败原因 @ApiModelProperty(value = "返回信息") public String msg; // 数据结果集 @ApiModelProperty(value = "数据结果集") public T data; public TelResult(int code, String msg, T data) { this.code = code; this.msg = msg; this.data = data; } public TelResult(int code, String msg) { this.code = code; this.msg = msg; } public int getCode() { return code; } public void setCode(int code) { this.code = code; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public T getData() { return data; } public void setData(T data) { this.data = data; } }
package com.sean.demo04.controller; import com.alibaba.fastjson.JSONObject; import com.sean.demo04.plugin.ApiJsonObject; import com.sean.demo04.plugin.ApiJsonProperty; import com.sean.demo04.utils.CurrentUser; import com.sean.demo04.utils.TelResult; import com.sean.demo04.vo.TelUserVO; import com.sean.demo04.vo.TestVo; import io.swagger.annotations.Api; import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiImplicitParams; import io.swagger.annotations.ApiOperation; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import springfox.documentation.annotations.ApiIgnore; import java.util.ArrayList; import java.util.List; @Api(value="用户controller",tags={"用户操作接口"}) @RestController @RequestMapping(value = "/index") public class IndexController { @ApiOperation(value = "登录" ,httpMethod = "POST") @RequestMapping(value = "/login",method = RequestMethod.POST) @ApiImplicitParams( @ApiImplicitParam(paramType = "header", name = "authorization", value = "Tonken", required = true, dataType = "String") ) public ResponseEntity<TelResult<TestVo>> login(@ApiIgnore @CurrentUser TelUserVO telUserVO, @ApiJsonObject(paramType="body",name = "json对象",value = { @ApiJsonProperty(paramType="query",required = true,key="name",example = "张三",description = "姓名",type = "String"), @ApiJsonProperty(paramType="query",required = false,key="age",example = "12",description = "年龄",type = "int") }) @RequestBody JSONObject dto) { String name = dto.get("name").toString(); TestVo testVo = new TestVo(); testVo.setName("用户:"+name+",登录成功。"); List list = new ArrayList(); list.add(testVo); list.add(testVo); //将返回值写入到请求对象中 // request.setAttribute(LoggerUtils.LOGGER_RETURN,obj); return new ResponseEntity<>(new TelResult(1,"成功",testVo), HttpStatus.OK); } }
## 登录 **接口地址** `/index/login` **请求方式** `POST` **consumes** `["application/json"]` **produces** `["*/*"]` **接口描述** `` **请求参数** | 参数名称 | 说明 | 参数类型 | 是否必须 | 类型 | schema | | ------------ | -------------------------------- |-----------|--------|----|--- | | authorization | Tonken | header | true | string | | | json对象 | 对象参数 | body | true | json对象 | json对象 | **schema属性说明** **json对象** | 参数名称 | 说明 | 参数类型 | 是否必须 | 类型 | schema | | ------------ | -------------------------------- |-----------|--------|----|--- | | age | 年龄 | body | false |integer(int32) | | | name | 姓名 | body | true |string | | **响应状态** | 状态码 | 说明 | schema | | ------------ | -------------------------------- |---------------------- | | 200 | OK |TelResult«TestVo» | | 201 | Created | | | 401 | Unauthorized | | | 403 | Forbidden | | | 404 | Not Found | | **响应参数** | 参数名称 | 说明 | 类型 | schema | | ------------ | -------------------|-------|----------- | | code | 状态码:1成功,0为失败 | integer(int32) | integer(int32) | | data |数据结果集 | TestVo | TestVo | | msg |返回信息 | string | | **schema属性说明** **TestVo** | 属性名称 | 说明 | 类型 | schema | | ------------ | ------------------|--------|----------- | | age | 年龄 | integer(int32) | | | name | 姓名 | string | | **响应示例** ```json { "code": 0, "data": { "age": 0, "name": "" }, "msg": "" }
## 搞了一下午终于可以丢掉文档了,这个完全满足前端调用接口需求
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。