GitHub 地址:https://github.com/JMCuixy/SwaggerToWord/tree/developer
原创作品,转载请注明出处:http://www.cnblogs.com/jmcui/p/8298823.html
一、前言
为什么会产生这个需求呢?
我们公司作为乙方,老是被客户追着要一份API文档,当我们把一个 Swagger 文档地址丢给客户的时候。客户还是很不满意,嫌不够正式!!死活坚持要一份 word 文档 。然后领导给了个接口模板,就把这个活交给我了......我去,近10个微服务,几百个接口,这不得要了我的命啊(最后整理出来将近200页的 word 文档)。最后,还是领导有办法:要不我们把Swagger的 json文件转成word文档吧!
一直坚持一句话。作为使用者,人要迁就机器;作为开发者,要机器迁就人。
二、思路
领导提供了一个接口模板,类似下面这样,其实就是一个word的table页。想到 html 可以转 word ,那么问题就变成了 :
1、解析JSON 文件
2、把JSON文件的内容填充进html 的Table中
3、由html直接转成word
几百个接口,一气呵成!如下,还有一个简单的示例,就是请求参数 和 返回值 。怎么处理呢?在程序中写了 HTTP 的请求,封装了需要的参数去执行了一个请求,得到相应的返回值!
三、实现
1、封装对象
按照面向对象的思想,一个接口Table就是一个对象,可变的请求参数和返回参数也封装成一个对象......
public class Table {
/**
* 大标题
*/
private String title;
/**
* 小标题
*/
private String tag;
/**
* url
*/
private String url;
/**
* 响应参数格式
*/
private String responseForm;
/**
* 请求方式
*/
private String requestType;
/**
* 请求体
*/
private List requestList;
/**
* 返回体
*/
private List responseList;
/**
* 请求参数
*/
private String requestParam;
/**
* 返回值
*/
private String responseParam;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getTag() {
return tag;
}
public void setTag(String tag) {
this.tag = tag;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getResponseForm() {
return responseForm;
}
public void setResponseForm(String responseForm) {
this.responseForm = responseForm;
}
public String getRequestType() {
return requestType;
}
public void setRequestType(String requestType) {
this.requestType = requestType;
}
public List getRequestList() {
return requestList;
}
public void setRequestList(List requestList) {
this.requestList = requestList;
}
public List getResponseList() {
return responseList;
}
public void setResponseList(List responseList) {
this.responseList = responseList;
}
public String getRequestParam() {
return requestParam;
}
public void setRequestParam(String requestParam) {
this.requestParam = requestParam;
}
public String getResponseParam() {
return responseParam;
}
public void setResponseParam(String responseParam) {
this.responseParam = responseParam;
}
}
Table
public class Request {
/**
* 请求参数
*/
private String description;
/**
* 参数名
*/
private String name;
/**
* 数据类型
*/
private String type;
/**
* 参数类型
*/
private String paramType;
/**
* 是否必填
*/
private Boolean require;
/**
* 说明
*/
private String remark;
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public Boolean getRequire() {
return require;
}
public void setRequire(Boolean require) {
this.require = require;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
public String getParamType() {
return paramType;
}
public void setParamType(String paramType) {
this.paramType = paramType;
}
}
Request
public class Response {
/**
* 返回参数
*/
private String description;
/**
* 参数名
*/
private String name;
/**
* 说明
*/
private String remark;
public Response(String description, String name, String remark) {
this.description = description;
this.name = name;
this.remark = remark;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
}
Response
2、解析 json
先来看看Swagger json文件的格式吧!需要注意的是这个 json 文件默认的 host 是没有加 http:// 前缀的,需要我们手动加上,因为程序的HTTP请求不像浏览器一样会自动补上 http:// 的前缀 ......
解析JSON真是一件枯燥的工作,大家可以按照自己想要生成模板的样子修改这边的代码......需要提的是,这里有一点让我纠结了好久。怎么伪造接口的请求参数发送HTTP请求以避免不会抛异常呢?最后还是参考了Swagger的方式,即:如果是 String 类型的参数,就把这个参数置为"string";如果是 Integer 类型的参数,就把这个参数置为 0 ;如果是Double 类型的参数,就置为 0.0 ;如果是其他没办法预见的类型,就全部置为 null;
解析 JSON 用的是Spring推荐的 jackson ,这部分感觉没什么好说的,直接上代码吧!
@Service
public class TableServiceImpl implements TableService {
@Override
public List
tableList() {
List list = new LinkedList();
try {
ClassLoader classLoader = TableService.class.getClassLoader();
URL resource = classLoader.getResource("data.json");
Map map = new ObjectMapper().readValue(resource, Map.class);
//得到host,用于模拟http请求
String host = String.valueOf(map.get("host"));
//解析paths
LinkedHashMap paths = (LinkedHashMap) map.get("paths");
if (paths != null) {
Iterator> iterator = paths.entrySet().iterator();
while (iterator.hasNext()) {
Table table = new Table();
List requestList = new LinkedList();
String requestType = "";
Map.Entry next = iterator.next();
String url = next.getKey();//得到url
LinkedHashMap value = next.getValue();
//得到请求方式,输出结果类似为 get/post/delete/put 这样
Set requestTypes = value.keySet();
for (String str : requestTypes) {
requestType += str + "/";
}
Iterator> it2 = value.entrySet().iterator();
//解析请求
Map.Entry get = it2.next();//得到get
LinkedHashMap getValue = get.getValue();
String title = (String) ((List) getValue.get("tags")).get(0);//得到大标题
String tag = String.valueOf(getValue.get("summary"));
//请求体
ArrayList parameters = (ArrayList) getValue.get("parameters");
if (parameters != null && parameters.size() > 0) {
for (int i = 0; i ) {
Request request = new Request();
LinkedHashMap param = (LinkedHashMap) parameters.get(i);
request.setDescription(String.valueOf(param.get("description")));
request.setName(String.valueOf(param.get("name")));
request.setType(String.valueOf(param.get("type")));
request.setParamType(String.valueOf(param.get("in")));
request.setRequire((Boolean) param.get("required"));
requestList.add(request);
}
}
//返回体,比较固定
List responseList = listResponse();
//模拟一次HTTP请求,封装请求体和返回体,如果是Restful的文档可以再补充
if (requestType.contains("post")) {
Map stringStringMap = toPostBody(requestList);
table.setRequestParam(stringStringMap.toString());
String post = NetUtil.post(host + url, stringStringMap);
table.setResponseParam(post);
} else if (requestType.contains("get")) {
String s = toGetHeader(requestList);
table.setResponseParam(s);
String getStr = NetUtil.get(host + url + s);
table.setResponseParam(getStr);
}
//封装Table
table.setTitle(title);
table.setUrl(url);
table.setTag(tag);
table.setResponseForm("application/json");
table.setRequestType(StringUtils.removeEnd(requestType, "/"));
table.setRequestList(requestList);
table.setResponseList(responseList);
list.add(table);
}
}
return list;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
//封装返回信息,可能需求不一样,可以自定义
private List listResponse() {
List responseList = new LinkedList();
responseList.add(new Response("受影响的行数", "counts", null));
responseList.add(new Response("结果说明信息", "msg", null));
responseList.add(new Response("是否成功", "success", null));
responseList.add(new Response("返回对象", "data", null));
responseList.add(new Response("错误代码", "errCode", null));
return responseList;
}
//封装post请求体
private Map toPostBody(List list) {
Map map = new HashMap(16);
if (list != null && list.size() > 0) {
for (Request request : list) {
String name = request.getName();
String type = request.getType();
switch (type) {
case "string":
map.put(name, "string");
break;
case "integer":
map.put(name, "0");
break;
case "double":
map.put(name, "0.0");
break;
default:
map.put(name, "null");
break;
}
}
}
return map;
}
//封装get请求头
private String toGetHeader(List list) {
StringBuffer stringBuffer = new StringBuffer();
if (list != null && list.size() > 0) {
for (Request request : list) {
String name = request.getName();
String type = request.getType();
switch (type) {
case "string":
stringBuffer.append(name+"&=string");
break;
case "integer":
stringBuffer.append(name+"&=0");
break;
case "double":
stringBuffer.append(name+"&=0.0");
break;
default:
stringBuffer.append(name+"&=null");
break;
}
}
}
String s = stringBuffer.toString();
if ("".equalsIgnoreCase(s)){
return "";
}
return "?" + StringUtils.removeStart(s, "&");
}
}
TableServiceImpl
3、html 模板
我们需要一个和 Word Table 模板一样的HTML 页面,然后利用JSP的 foreach 遍历后台得到的 List
集合,一气呵成,生成所有接口......
-- text/html:正常的html显示 application/msword:html页面直接转word--%>
@ page contentType="application/msword" pageEncoding="UTF-8" language="java" %>
--%@page contentType="text/html" pageEncoding="UTF-8" language="java" %>--%>
@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
DOCTYPE html>
html>
head>
title>tooltitle>
style type="text/css">
.bg {
background-color: rgb(84, 127, 177);
}
tr {
height: 20px;
font-size: 12px;
}
.specialHeight {
height: 40px;
}
style>
head>
body>
div style="width:800px; margin: 0 auto">
c:forEach items="${table}" var="t">
h4>${t.title}h4> --这个是类的说明--%>
h5>${t.tag}h5> --这个是每个请求的说明,方便生成文档后进行整理--%>
table border="1" cellspacing="0" cellpadding="0" width="100%">
tr class="bg">
td colspan="6">c:out value="${t.tag}"/>td>
tr>
tr>
td>URLtd>
td colspan="5">${t.url}td>