springboot集成swagger加强版,接受Map与JSONObject参数,返回实体注释
1.集成swagger
a.导入jar包
<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>
b.配置SwaggerConfig
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
public class SwaggerConfig {
@Bean
public Docket customDocket() {
return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo());
}
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
2.swagger配置接受JSONObject类型数据
a.配置插件
主类
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
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)) {
Optional<ApiJsonObject> optional = methodParameter.findAnnotation(ApiJsonObject.class);
if (optional.isPresent()) {
String name = optional.get().name();
String paramType = optional.get().paramType();
ApiJsonProperty[] properties = optional.get().value();
parameterContext.getDocumentationContext().getAdditionalModels().add(typeResolver.resolve(createRefModel(properties, name)));
parameterContext.parameterBuilder()
.parameterType(paramType)
.modelRef(new ModelRef(name)).description("对象参数")
.name(name);
}
}
}
private final static String basePackage = "com.sean.demo04.swagger.model.";
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;
}
}
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())));
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();
String example() default "";
String type() default "String";
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();
}
3.swagger返回实体注释
返回实体必须加泛型
package com.sean.demo04.utils;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
@ApiModel
public class TelResult<T>{
@ApiModelProperty(value = " 状态码:1成功,0为失败")
public int code;
@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;
}
}
4.测试controller
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);
return new ResponseEntity<>(new TelResult(1,"成功",testVo), HttpStatus.OK);
}
}
5.测试结果
## 登录
**接口地址** `/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": ""
}
## 搞了一下午终于可以丢掉文档了,这个完全满足前端调用接口需求