springboot集成swagger加强版,接受Map与JSONObject参数,返回实体注释

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  //第三方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

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   //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();//请求类型

}

3.swagger返回实体注释

返回实体必须加泛型
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;
    }
}

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);
        //将返回值写入到请求对象中
//        request.setAttribute(LoggerUtils.LOGGER_RETURN,obj);
        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": ""
}

## 搞了一下午终于可以丢掉文档了,这个完全满足前端调用接口需求
  • 5
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值