现在写接口文档都比较费劲,后来出现了swagger在线文档,是一个多么好用的东西啊,当时这个东西上线了就不能开启了,如果甲方需要接口文档当然那边要是觉得在线的也可以就给他们,如果人家说这个不行我们就用swagger生成word离线文档
1. 引入pom jar包
<!-- https://mvnrepository.com/artifact/io.github.swagger2markup/swagger2markup -->
<dependency>
<groupId>io.github.swagger2markup</groupId>
<artifactId>swagger2markup</artifactId>
<version>1.3.3</version>
</dependency>
如果下不下来手动下载jar放在仓库里面就好了
2.开始我们愉快的编写代码
- 创建实体类
SwaggerModel
import java.util.List;
/**
* @Author:zhuzi
* @Explanation:
*/
public class SwaggerModel {
/**
* 大标题
*/
private String title;
/**
* 小标题
*/
private String tag;
/**
* url
*/
private String url;
/**
* 描述
*/
private String description;
/**
* 请求参数格式
*/
private String requestForm;
/**
* 响应参数格式
*/
private String responseForm;
/**
* 请求方式
*/
private String requestType;
/**
* 请求体
*/
private List<Request> requestList;
/**
* 返回体
*/
private List<Response> responseList;
/**
* 请求参数
*/
private String requestParam;
/**
* 返回参数
*/
private String responseParam;
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getRequestForm() {
return requestForm;
}
public void setRequestForm(String requestForm) {
this.requestForm = requestForm;
}
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<Request> getRequestList() {
return requestList;
}
public void setRequestList(List<Request> requestList) {
this.requestList = requestList;
}
public List<Response> getResponseList() {
return responseList;
}
public void setResponseList(List<Response> 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;
}
}
Response
/**
* @Author:zhuzi
* @Explanation:
*/
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 Response() {
}
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;
}
}
Request
/**
* @Author:zhuzi
* @Explanation:
*/
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;
}
}
- 添加几个工具类
JsonUtils
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import java.io.IOException;
import java.util.List;
/**
* @Author:zhuzi
* @Explanation:
*/
public class JsonUtils {
private static ObjectMapper objectMapper = new ObjectMapper();
static {
objectMapper.setSerializationInclusion(JsonInclude.Include.ALWAYS);
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);
objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
}
public static <T> T readValue(String jsonStr, Class<T> clazz) throws IOException {
return objectMapper.readValue(jsonStr, clazz);
}
public static <T> List<T> readListValue(String jsonStr, Class<T> clazz) throws IOException {
JavaType javaType = objectMapper.getTypeFactory().constructParametricType(List.class, clazz);
return objectMapper.readValue(jsonStr, javaType);
}
public static ArrayNode readArray(String jsonStr) throws IOException {
JsonNode node = objectMapper.readTree(jsonStr);
if (node.isArray()) {
return (ArrayNode) node;
}
return null;
}
public static JsonNode readNode(String jsonStr) throws IOException {
return objectMapper.readTree(jsonStr);
}
public static String writeJsonStr(Object obj) throws JsonProcessingException {
return objectMapper.writeValueAsString(obj);
}
public static ObjectNode createObjectNode() {
return objectMapper.createObjectNode();
}
public static ArrayNode createArrayNode() {
return objectMapper.createArrayNode();
}
}
MenuUtils
/**
* @Author:zhuzi
* @Explanation:
*/
public class MenuUtils {
public static Integer count = 0;
public static String menuStr = "null";
public static boolean isMenu(String tags) {
if (menuStr.equals(tags)) {
count++;
} else {
menuStr = tags;
count = 0;
}
if (count == 0) {
return true;
} else {
return false;
}
}
}
- 添加restTemplate配置
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.springframework.context.annotation.Bean;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import javax.net.ssl.SSLContext;
import java.nio.charset.StandardCharsets;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
/**
* @Author:zhuzi
* @Explanation:
*/
@Component
public class JavaConfig {
@Bean
public RestTemplate restTemplate() throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
TrustStrategy acceptingTrustStrategy = (X509Certificate[] chain, String authType) -> true;
SSLContext sslContext = org.apache.http.ssl.SSLContexts.custom()
.loadTrustMaterial(null, acceptingTrustStrategy)
.build();
SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext);
CloseableHttpClient httpClient = HttpClients.custom()
.setSSLSocketFactory(csf)
.build();
HttpComponentsClientHttpRequestFactory requestFactory =
new HttpComponentsClientHttpRequestFactory();
requestFactory.setHttpClient(httpClient);
//60s
requestFactory.setConnectTimeout(60 * 1000);
requestFactory.setReadTimeout(60 * 1000);
RestTemplate restTemplate = new RestTemplate(requestFactory);
restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));
return restTemplate;
}
}
- 编写service
SwaggerService
import java.util.List;
/**
* @Author:zhuzi
* @Explanation:
*/
public interface SwaggerService {
List<SwaggerModel> tableList(String jsonUrl);
}
SwaggerServiceImpl
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.web.client.RestTemplate;
import java.io.IOException;
import java.util.*;
@Service("swagger")
public class SwaggerServiceImpl implements SwaggerService {
@Autowired
private RestTemplate restTemplate;
@Value("${swagger.url}")
private String swaggerUrl;
@Override
public List<SwaggerModel> tableList(String jsonUrl) {
jsonUrl = Optional.ofNullable(jsonUrl).orElse(swaggerUrl);
List<SwaggerModel> result = new ArrayList<>();
try {
String jsonStr = restTemplate.getForObject(jsonUrl, String.class);
// convert JSON string to Map
Map<String, Object> map = JsonUtils.readValue(jsonStr, HashMap.class);
//解析paths
Map<String, LinkedHashMap> paths = (LinkedHashMap) map.get("paths");
if (paths != null) {
Iterator<Map.Entry<String, LinkedHashMap>> it = paths.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, LinkedHashMap> path = it.next();
Iterator<Map.Entry<String, LinkedHashMap>> it2 = path.getValue().entrySet().iterator();
// 1.请求路径
String url = path.getKey();
// 2.请求方式,类似为 get,post,delete,put 这样
String requestType = StringUtils.join(path.getValue().keySet(), ",");
//不管有几种请求方式,都只解析第一种
Map.Entry<String, LinkedHashMap> firstRequest = it2.next();
Map<String, Object> content = firstRequest.getValue();
// 4. 大标题(类说明)
String title = String.valueOf(((List) content.get("tags")).get(0));
// 5.小标题 (方法说明)
String tag = String.valueOf(content.get("summary"));
// 6.接口描述
String description = String.valueOf(content.get("summary"));
// 7.请求参数格式,类似于 multipart/form-data
String requestForm = "";
List<String> consumes = (List) content.get("consumes");
if (consumes != null && consumes.size() > 0) {
requestForm = StringUtils.join(consumes, ",");
}
// 8.返回参数格式,类似于 application/json
String responseForm = "";
List<String> produces = (List) content.get("produces");
if (produces != null && produces.size() > 0) {
responseForm = StringUtils.join(produces, ",");
}
// 9. 请求体
List<Request> requestList = new ArrayList<>();
List<LinkedHashMap> parameters = (ArrayList) content.get("parameters");
if (!CollectionUtils.isEmpty(parameters)) {
for (Map<String, Object> param : parameters) {
Request request = new Request();
request.setName(String.valueOf(param.get("name")));
Object in = param.get("in");
if (in != null && "body".equals(in)) {
request.setType(String.valueOf(in));
Map<String, Object> schema = (Map) param.get("schema");
Object ref = schema.get("$ref");
// 数组情况另外处理
if (schema.get("type") != null && "array".equals(schema.get("type"))) {
ref = ((Map) schema.get("items")).get("$ref");
}
request.setParamType(ref == null ? "{}" : ref.toString());
} else {
request.setType(param.get("type") == null ? "Object" : param.get("type").toString());
request.setParamType(String.valueOf(in));
}
request.setRequire((Boolean) param.get("required"));
request.setRemark(String.valueOf(param.get("description")));
requestList.add(request);
}
}
// 10.返回体
List<Response> responseList = new ArrayList<>();
Map<String, Object> responses = (LinkedHashMap) content.get("responses");
Iterator<Map.Entry<String, Object>> it3 = responses.entrySet().iterator();
while (it3.hasNext()) {
Response response = new Response();
Map.Entry<String, Object> entry = it3.next();
// 状态码 200 201 401 403 404 这样
response.setName(entry.getKey());
LinkedHashMap<String, Object> statusCodeInfo = (LinkedHashMap) entry.getValue();
response.setDescription(String.valueOf(statusCodeInfo.get("description")));
response.setRemark(String.valueOf(statusCodeInfo.get("description")));
responseList.add(response);
}
//封装Table
SwaggerModel table = new SwaggerModel();
//是否添加为菜单
if (MenuUtils.isMenu(title)) {
table.setTitle(title);
}
table.setUrl(url);
table.setTag(tag);
table.setDescription(description);
table.setRequestForm(requestForm);
table.setResponseForm(responseForm);
table.setRequestType(requestType);
table.setResponseList(responseList);
table.setRequestParam(JsonUtils.writeJsonStr(buildParamMap(requestList, map)));
for (Request request : requestList) {
request.setParamType(request.getParamType().replaceAll("#/definitions/", ""));
}
table.setRequestList(requestList);
// 取出来状态是200时的返回值
Object obj = responses.get("200");
if (obj == null) {
table.setResponseParam("");
result.add(table);
continue;
}
Object schema = ((Map) obj).get("schema");
if (schema == null) {
table.setResponseParam("");
result.add(table);
continue;
}
if (((Map) schema).get("$ref") != null) {
//非数组类型返回值
String ref = (String) ((Map) schema).get("$ref");
//解析swagger2 ref链接
ObjectNode objectNode = parseRef(ref, map);
table.setResponseParam(objectNode.toString());
result.add(table);
continue;
}
Object items = ((Map) schema).get("items");
if (items != null && ((Map) items).get("$ref") != null) {
//数组类型返回值
String ref = (String) ((Map) items).get("$ref");
//解析swagger2 ref链接
ObjectNode objectNode = parseRef(ref, map);
ArrayNode arrayNode = JsonUtils.createArrayNode();
arrayNode.add(objectNode);
table.setResponseParam(arrayNode.toString());
result.add(table);
continue;
}
result.add(table);
}
}
} catch (Exception e) {
}
return result;
}
/**
* 从map中解析出指定的ref
*
* @param ref ref链接 例如:"#/definitions/PageInfoBT«Customer»"
* @param map 是整个swagger json转成map对象
* @return
* @author fpzhan
*/
private ObjectNode parseRef(String ref, Map<String, Object> map) {
ObjectNode objectNode = JsonUtils.createObjectNode();
if (StringUtils.isNotEmpty(ref) && ref.startsWith("#")) {
String[] refs = ref.split("/");
Map<String, Object> tmpMap = map;
//取出ref最后一个参数 start
for (String tmp : refs) {
if (!"#".equals(tmp)) {
tmpMap = (Map<String, Object>) tmpMap.get(tmp);
}
}
//取出ref最后一个参数 end
//取出参数
if (tmpMap == null) {
return objectNode;
}
Object properties = tmpMap.get("properties");
if (properties == null) {
return objectNode;
}
Map<String, Object> propertiesMap = (Map<String, Object>) properties;
Set<String> keys = propertiesMap.keySet();
//遍历key
for (String key : keys) {
Map<String, Object> keyMap = (Map) propertiesMap.get(key);
if ("array".equals(keyMap.get("type"))) {
//数组的处理方式
String sonRef = (String) ((Map) keyMap.get("items")).get("$ref");
//对象自包含,跳过解析
if (ref.equals(sonRef)) {
continue;
}
JsonNode jsonNode = parseRef(sonRef, map);
ArrayNode arrayNode = JsonUtils.createArrayNode();
arrayNode.add(jsonNode);
objectNode.set(key, arrayNode);
} else if (keyMap.get("$ref") != null) {
//对象的处理方式
String sonRef = (String) keyMap.get("$ref");
//对象自包含,跳过解析
if (ref.equals(sonRef)) {
continue;
}
ObjectNode object = parseRef(sonRef, map);
objectNode.set(key, object);
} else {
//其他参数的处理方式,string、int
String str = "";
if (keyMap.get("description") != null) {
str = str + keyMap.get("description");
}
if (keyMap.get("format") != null) {
str = str + String.format("格式为(%s)", keyMap.get("format"));
}
objectNode.put(key, str);
}
}
}
return objectNode;
}
/**
* 封装post请求体
*
* @param list
* @param map
* @return
*/
private Map<String, Object> buildParamMap(List<Request> list, Map<String, Object> map) throws IOException {
Map<String, Object> paramMap = new HashMap<>(8);
if (list != null && list.size() > 0) {
for (Request request : list) {
String name = request.getName();
String type = request.getType();
switch (type) {
case "string":
paramMap.put(name, "string");
break;
case "integer":
paramMap.put(name, 0);
break;
case "number":
paramMap.put(name, 0.0);
break;
case "boolean":
paramMap.put(name, true);
break;
case "body":
String paramType = request.getParamType();
ObjectNode objectNode = parseRef(paramType, map);
paramMap = JsonUtils.readValue(objectNode.toString(), Map.class);
break;
default:
paramMap.put(name, null);
break;
}
}
}
return paramMap;
}
- 实现controller
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.client.RestTemplate;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.List;
/**
* @Author:zhuzi
* @Explanation:
*/
@Controller
public class SwaggerController {
@Autowired
@Qualifier("swagger")
private SwaggerService swaggerService;
@Autowired
private RestTemplate restTemplate;
@Value("${server.port}")
private Integer port;
/**
* 将 swagger 文档转换成 html 文档,可通过在网页上右键另存为 xxx.doc 的方式转换为 word 文档
*
* @param model
* @param url 需要转换成 word 文档的资源地址
* @return
*/
@Deprecated
@GetMapping("/toWord")
public String getWord(Model model, @RequestParam(value = "url", required = false) String url) {
List<SwaggerModel> tables = swaggerService.tableList(url);
model.addAttribute("tables", tables);
return "word";
}
/**
* 将 swagger 文档一键下载为 doc 文档
*
* @param url 需要转换成 word 文档的资源地址
* @param response
*/
@RequestMapping("/downloadWord")
public void word(@RequestParam String url, HttpServletResponse response) {
ResponseEntity<String> forEntity = restTemplate.getForEntity("http://localhost:" + port + "/toWord?url=" + url, String.class);
response.setContentType("application/octet-stream;charset=utf-8");
response.setCharacterEncoding("utf-8");
try (BufferedOutputStream bos = new BufferedOutputStream(response.getOutputStream())) {
response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode("toWord.doc", "utf-8"));
byte[] bytes = forEntity.getBody().getBytes();
bos.write(bytes, 0, bytes.length);
bos.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
- 配置yml配置文件
swagger:
url: http://localhost:8100/v2/api-docs
3.测试 访问: http://localhost:8080/toWord
可以看到
输入:http://localhost:8080/downloadWord?url=http://localhost:8100/v2/api-docs
这样就可以下周再下来了
注意:如果有编码问题 打开设置 -> file encodings 将编码全部设置为 UTF-8