knife4j word导出模板改造经验

由于默认的导出模板形式不符合公司文档格式要求,最典型的就是要求增加每个字段的长度,因此

在官方文档中找到knife4j-vue,进行定制开发,

主要更改文件涉及到Knife4jAsync.js(为字段长度进行赋值)及wordTransform.js(定义输出文档格式)

swagger默认调用后端的接口

Request URL:

http://192.168.1.6:8080/v2/api-docs?group=1.0

接口返回报文格式如下,可以看到很多注解字段后端并没有返回给前端,意味着前端不能用到这些字段,得找到一个字段用于表示数据长度,

{
	"swagger": "2.0",
	"info": {
		"description": "# swagger-bootstrap-ui-demo RESTful APIs",
		"version": "1.0",
		"title": "Survey RESTful APIs",
		"termsOfService": "http://localhost:8080/"
	},
	"host": "192.168.1.6:8080",
	"basePath": "/",
	"tags": [{
		"name": "UserController_2",
		"x-author": "wy",
		"x-order": "2147483647"
	}],
	"paths": {
		"/user/saveUser": {
			"post": {
				"tags": ["UserController_2"],
				"summary": "保存测试接口",
				"description": "保存测试接口222",
				"operationId": "saveUserUsingPOST",
				"consumes": ["application/json"],
				"produces": ["*/*"],
				"parameters": [{
					"in": "body",
					"name": "user",
					"description": "user",
					"required": true,
					"schema": {
						"originalRef": "User",
						"$ref": "#/definitions/User"
					}
				}],
				"responses": {
					"200": {
						"description": "OK",
						"schema": {
							"originalRef": "响应信息主体«UserResponse»",
							"$ref": "#/definitions/响应信息主体«UserResponse»"
						}
					},
					"201": {
						"description": "Created"
					},
					"401": {
						"description": "Unauthorized"
					},
					"403": {
						"description": "Forbidden"
					},
					"404": {
						"description": "Not Found"
					}
				},
				"deprecated": false,
				"x-order": "2147483647"
			}
		}
	},
	"definitions": {
		"User": {
			"type": "object",
			"required": ["message"],
			"properties": {
				"message": {
					"type": "string",
					"example": 96,
					"description": "入参信息"
				},
				"id": {
					"type": "string",
					"example": 1,
					"description": "主键",
					"enum": ["32"]
				}
			},
			"title": "User"
		},
		"UserResponse": {
			"type": "object",
			"required": ["message", "name"],
			"properties": {
				"message": {
					"type": "string",
					"example": 9600,
					"description": "入参信息"
				},
				"name": {
					"type": "string",
					"example": 1200,
					"description": "用户名"
				}
			},
			"title": "UserResponse"
		},
		"响应信息主体«UserResponse»": {
			"type": "object",
			"required": ["code", "data"],
			"properties": {
				"code": {
					"type": "integer",
					"format": "int32",
					"example": 200,
					"description": "返回标记:成功标记=200,失败标记=-1"
				},
				"data": {
					"example": "null",
					"description": "返回数据",
					"originalRef": "UserResponse",
					"$ref": "#/definitions/UserResponse"
				},
				"msg": {
					"type": "string",
					"example": "请求成功",
					"description": "返回信息"
				}
			},
			"title": "响应信息主体«UserResponse»"
		}
	},
	"x-openapi": {
		"x-markdownFiles": [{
			"name": "项目文档",
			"children": [{
				"title": "剑指 Offer 16. 数值的整数次方(快速幂).md",
				"content": "#### [剑指 Offer 16. 数值的整数次方](https://leetcode-cn.com/problems/shu-zhi-de-zheng-shu-ci-fang-lcof/)(快速幂)\n\n![image-20210918141143858](C:\\Users\\WY\\AppData\\Roaming\\Typora\\typora-user-images\\image-20210918141143858.png)\n\n哪个位是1,就乘对应的权重值\n\n```java\nclass Solution {\n    public double myPow(double x, int n) {\n        if(x == 0) return 0;\n        long b = n;\n        double res = 1.0;\n        if(b < 0) {\n            x = 1 / x;\n            b = -b;\n        }\n        while(b > 0) {\n            if((b & 1) == 1) res *= x;\n            x *= x;\n            b >>= 1;\n        }\n        return res;\n    }\n}\n```\n\n**递归解法(用这个):**\n\n```java\nclass Solution {\n    public double myPow(double x, int n) {\n        if(n<0){\n            n = -n;\n            x = 1.0/x;\n        }\n        return qsm(x,n);\n    }\n\n    double qsm(double x, int n){\n        if(x==0){\n            return 0;\n        }else if(n==0){\n            return 1;\n        }\n        double ans = qsm(x,n/2);\n        return n%2==0?ans*ans:ans*ans*x;\n    }\n}\n```\n\n"
			}]
		}],
		"x-setting": {
			"language": "zh-CN",
			"enableSwaggerModels": true,
			"swaggerModelName": "Swagger Models",
			"enableReloadCacheParameter": false,
			"enableAfterScript": true,
			"enableDocumentManage": true,
			"enableVersion": false,
			"enableRequestCache": true,
			"enableFilterMultipartApis": false,
			"enableFilterMultipartApiMethodType": "POST",
			"enableHost": true,
			"enableHostText": ""
		}
	}
}

首先要知道@ApiModelProperty是swagger的注解,它的作用是添加和操作属性模块的数据,下面是它内部的常用属性:

1、value()

源码:String value() default "";
1
参数类型为String,作用为此属性的简要描述。

2、name()

源码: String name() default "";
1
参数类型为String,作用为允许重写属性的名称。

3、allowableValues()

源码:String allowableValues() default "";
参数类型为String,作用为限制此参数存储的长度。

4、access()

源码:String access() default "";
参数类型为String,作用为允许从API文档中过滤属性

5、notes()

源码: String notes() default "";
参数类型为String,作用为该字段的注释说明

6、dataType()

源码: String dataType() default "";
参数类型为String,作用为参数的数据类型。

7、required()

源码:boolean required() default false;
参数类型为String,作用为指定参数是否可以为空,默认为false

8、 position()

源码:int position() default 0;
参数类型为int,作用为允许显式地对模型中的属性排序。

9、hidden()

源码:boolean hidden() default false;
参数类型为boolean,作用为是否允许模型属性隐藏在Swagger模型定义中,默认为false。

10、example()

源码:String example() default "";
参数为String类型,作用为属性的示例值。

11、readOnly()

源码:boolean readOnly() default false;
参数类型为boolean,作用为是否允许将属性指定为只读,默认为false。

12、reference()

源码: String reference() default "";
参数类型为String,作用为指定对对应类型定义的引用,重写指定的任何其他数据名称。

13、allowEmptyValue()

源码:boolean allowEmptyValue() default false;
参数类型为boolean,作用为是否允许传递空值,默认为false

示例:

@Data
public class User {
    @ApiModelProperty(value = "主键",name = "id",
            allowableValues = "32",
            access = "1",
            notes = "用户的id",
            dataType = "int",
            required = false,
            position = 1,
            hidden = false,
            example = "1",
            readOnly = false,
            reference = "id",
            allowEmptyValue = false)
    private String id;

    @ApiModelProperty(required = true, notes = "入参信息", example = "96")
    private String message;

}

后端代码用例 knife4j-example: knife4j的小demo

knife4j UI包是把前端文件打成webJar包,在项目中以jar包形式引用形式,

最终改好的前端打webJar包,然后在项目中去除旧包依赖,加上新UI打成的本地JAR包即可解决定制问题,下图中是增加利用了自定义文档功能,主要用户附录,修改记录之类的说明

一:如何解决泛型多个不同类型结果

@ApiResponses说明方法多个返回值,注意在创建swagger版本要指定版本为

return new Docket(DocumentationType.SWAGGER_2),sawgger3 jar包是支持向下兼容的,不需要降低swagger版本,如果一个接口既有swagger2也有OAS_30版本 就会出现2个分组,如果你创建OAS_30版本,注释用swagger2注解比如ApiResponse,就不会产生多态效果,这是一个springfox BUG,官网2年没更新了。

可同时说明多个错误码的响应内容

@ApiResponses({
            @ApiResponse(code = 200, message = "SUCCESS", response = ResultListGenericity.class),
            @ApiResponse(code = 400, message = "未授权"),
            @ApiResponse(code = 401, message = "授权过期")
    })
PS:response返回的内容一般是 Void.class ,为了返回泛型类或List<T> ,需要创建一个新类去继承要返回的类,然后response中返回新类即可。

如:

@ApiResponse(code = 200, message = "SUCCESS", response = ResultGenericity.class)
public Result detail(@PathVariable Integer id) {
        return ResultGenerator.genSuccessResult(new User());
    }
 
private static class ResultGenericity extends Result<User> {
}

使用泛型时,如果只存在一种格式的返回,可以在返回结果写死类型,如果存在多个不同格式,需要使用@ApiResponses用于标记不同的返回结果样式

同时在前端修改,吧默认的响应状态码改为我们想要的返回结果

 @ApiOperation(value = "保存测试接口", notes = "保存测试接口notes",position=2)
    @PostMapping("saveUser")
    @ApiOperationSupport(order=2,author = "{\n" +
            "  \"data\": zsasasa,\n" +
            "  \"message\": zssssss,\n" +
            "  \"id\": 1\n" +
            "}")
    @ApiResponses({
            @ApiResponse(code = 200, message = "盘点", response = Demo1.class),
            @ApiResponse(code = 400, message = "理货",response = Demo2.class),
    })
    public <T> R<UserResponse> saveUser(@RequestBody User<T> user){
        logger.info("传入的user对象为:{}", user);
        ResultBO results = new ResultBO();

        results.setCode(200);
        results.setContent(user);
        results.setMsg("调用测试接口成功!");
        results.setSucceed(true);
        logger.info("调用测试接口成功");
        return R.ok(new UserResponse());
    }

function createWordApiResponseParameters(apiInfo, markdownCollections) {
  //判断是否多个schema
  if (apiInfo.multipartResponseSchema) {
    var multipartData = apiInfo.multipCodeDatas;
    if (KUtils.arrNotEmpty(multipartData)) {
      multipartData.forEach(function (resp) {
        console.log("返回结果"+resp)
        wordLines(markdownCollections);
        markdownCollections.push('<div class="knife4j-word-title">返回结果-'+KUtils.toString(resp.description, '')+'</div>');
        //markdownCollections.push('**响应状态码-' + KUtils.toString(resp.code, '') + '**:');原先文档输出
        createWordApiResponseSingleParam(resp, markdownCollections);
      })
    }
  } else {
    //单个
    createWordApiResponseSingleParam(apiInfo.multipData, markdownCollections);
  }
}

但是如果面对请求参数带泛型的,我的处理方式是利用扩展注解中author字段,先在里面写上描述文字,然后稍微改一下前端文件即可

/*  if(KUtils.strNotBlank(apiInfo.author)){
    markdownCollections.push('<div class="knife4j-word-title">开发者</div>');
    markdownCollections.push('<div class="knife4j-word-content">'+KUtils.toString(apiInfo.author, '暂无')+'</div>');
  }*/

  //判断是否有请求示例
  if (KUtils.checkUndefined(apiInfo.requestValue)) {
    //如果存在多态,则用扩展字段author代替
    if(KUtils.strNotBlank(apiInfo.author)){
      apiInfo.requestValue=apiInfo.author;
    }

 

     二:knife4j 3.0.3 swagger2 自定义增强文档不生效

这是bug,目前可以修改源码以让其支持swagger2版本的自定义文档,源码是全部都按data['x-openapi'];情况进行判断了,没有考虑向下兼容

SwaggerBootstrapUi.prototype.openDocuments=function(data){
  var that=this;
  debugger
  //判断是否包含x-openapi的增强
  var openapi=data['x-openapi'];
  var mkdFiles;
  if(KUtils.checkUndefined(openapi)) {
     mkdFiles=openapi['x-markdownFiles'];
  }else{
     mkdFiles=data['x-markdownFiles'];
  }
      var currentInstanceMarkdownFileMap={};
      mkdFiles.forEach(mdTag=>{

在改造过程中,也会存在一些问题,比如默认的swagger注解不够用的问题,默认的swagger后端并不会返回的注解属性内容,如果后面再增加一项说明,就需要考虑改后端swagger框架,

参考官网资料:knife4j

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zhousenshan

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值