写在前面:

完全通过Spring Boot工程 Java代码,将swagger json 一键解析为html页面、导出word和execel的解析算法,不需要任何网上那些类似于“SwaggerMarkup2”等插件来实现。

由于业务需要,准备开发一个openapi开放平台,类似于阿里巴巴的CSB云服务总线项目,用于企业内外服务能力的打通和统一开放管理,提供独特的跨环境服务级联机制和常见协议适配支持,实现了对api接口的对外发布和订阅审核,让企业内外都能够更方便的使用到api接口。

其中需要实现一个核心功能,服务的导入功能,通过swagger json将我们其他项目中已经写好的接口一键导入到这个api开放平台并生成api接口详情页,那么这就需要实现一个swagger json解析的操作。

下面马上进入正题,本文主要也是分享一下,自己解析swagger json为html、word等功能的代码、思路以及界面。

页面效果展示:

将下图这样的swagger json解析出一个个api接口详情页。

解析前:

解析后:

通过json解析完可以显示所有的接口信息,如图:

这是单个接口的api详情信息,如下图:

点击按钮导出api详情页为word的效果展示,如图:

导出word:

Swagger Json格式详解:

代码部分:

我这边实现两种思路,一是直接解析swagger json然后直接存入实体类生成为html,还要一种是建立好实体类以及数据库表后,将swagger json解析入库入表做持久化,再通过表中数据渲染到页面上。

下面我是介绍的swagger json入库入表再渲染为html的方案。

步骤大概是:首先定义好建好表,写好实体类后,再开始实现swagger json解析的算法。

实体类定义:

服务资源表:

@ApiModel(value = "服务资源表", description = "服务资源表")
public class ServiceResource implements Serializable{ /**
* 程序序列化ID
*/
private static final long serialVersionUID=1L; /**
* 服务ID
*/ @ApiModelProperty(value = "服务ID")
private String id; /**
* 租户
*/ @ApiModelProperty(value = "租户")
private String gmtTenant; /**
* 创建时间
*/ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@ApiModelProperty(value = "创建时间")
private Date gmtCreate;
public Date getGmtCreate(){
return gmtCreate==null?null:(Date) gmtCreate.clone();
} public void setGmtCreate(Date gmtCreate){
this.gmtCreate = gmtCreate==null?null:(Date) gmtCreate.clone();
} /**
* 创建人
*/ @ApiModelProperty(value = "创建人")
private String gmtCreator; /**
* 创建人名称
*/ @ApiModelProperty(value = "创建人名称")
private String gmtCrtname; /**
* 最后修改时间
*/ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@ApiModelProperty(value = "最后修改时间")
private Date gmtModified;
public Date getGmtModified(){
return gmtModified==null?null:(Date) gmtModified.clone();
} public void setGmtModified(Date gmtModified){
this.gmtModified = gmtModified==null?null:(Date) gmtModified.clone();
} /**
* 最后修改人
*/ @ApiModelProperty(value = "最后修改人")
private String gmtModifiedby; /**
* 最后修改人名称
*/ @ApiModelProperty(value = "最后修改人名称")
private String gmtMfyname; /**
* 服务名称
*/ @ApiModelProperty(value = "服务名称")
private String serviceName; /**
* 请求地址
*/ @ApiModelProperty(value = "请求地址")
private String requestUrl; /**
* 请求方法
*/ @ApiModelProperty(value = "请求方法")
private String requestMethod; /**
* 请求格式
*/ @ApiModelProperty(value = "请求类型")
private String contentType; /**
* 返回类型
*/ @ApiModelProperty(value = "返回类型")
private String callContentType; /**
* 服务描述
*/ @ApiModelProperty(value = "服务描述")
private String serviceDesc; /**
* 服务版本
*/ @ApiModelProperty(value = "服务版本")
private String serviceVersion; /**
* 是否有效
*/ @ApiModelProperty(value = "是否有效")
private Integer isValid; /**
* 是否发布
*/ @ApiModelProperty(value = "是否发布")
private Integer isRelease; /**
* 是否发布
*/ @ApiModelProperty(value = "是否需要授权访问")
private Integer isAuthorizedAccess; /**
* 操作id
*/
@ApiModelProperty(value = "操作id")
private String operationId; private Integer isDelete; private String routeUuid; private String currentCatalogId; public static long getSerialVersionUID() {
return serialVersionUID;
} public String getId() {
return id;
} public void setId(String id) {
this.id = id;
} public String getGmtTenant() {
return gmtTenant;
} public void setGmtTenant(String gmtTenant) {
this.gmtTenant = gmtTenant;
} public String getGmtCreator() {
return gmtCreator;
} public void setGmtCreator(String gmtCreator) {
this.gmtCreator = gmtCreator;
} public String getGmtCrtname() {
return gmtCrtname;
} public void setGmtCrtname(String gmtCrtname) {
this.gmtCrtname = gmtCrtname;
} public String getGmtModifiedby() {
return gmtModifiedby;
} public void setGmtModifiedby(String gmtModifiedby) {
this.gmtModifiedby = gmtModifiedby;
} public String getGmtMfyname() {
return gmtMfyname;
} public void setGmtMfyname(String gmtMfyname) {
this.gmtMfyname = gmtMfyname;
} public String getServiceName() {
if(!StringUtils.isEmpty(serviceName)){
return serviceName.replaceAll(" ", "");
}
return serviceName;
} public void setServiceName(String serviceName) {
this.serviceName = serviceName;
} public String getRequestUrl() {
return requestUrl;
} public void setRequestUrl(String requestUrl) {
this.requestUrl = requestUrl;
} public String getRequestMethod() {
return requestMethod;
} public void setRequestMethod(String requestMethod) {
this.requestMethod = requestMethod;
} public String getContentType() {
return contentType;
} public void setContentType(String contentType) {
this.contentType = contentType;
} public String getCallContentType() {
return callContentType;
} public void setCallContentType(String callContentType) {
this.callContentType = callContentType;
} public String getServiceDesc() {
return serviceDesc;
} public void setServiceDesc(String serviceDesc) {
this.serviceDesc = serviceDesc;
} public String getServiceVersion() {
return serviceVersion;
} public void setServiceVersion(String serviceVersion) {
this.serviceVersion = serviceVersion;
} public Integer getIsValid() {
return isValid;
} public void setIsValid(Integer isValid) {
this.isValid = isValid;
} public Integer getIsRelease() {
return isRelease;
} public void setIsRelease(Integer isRelease) {
this.isRelease = isRelease;
} public Integer getIsAuthorizedAccess() {
return isAuthorizedAccess;
} public void setIsAuthorizedAccess(Integer isAuthorizedAccess) {
this.isAuthorizedAccess = isAuthorizedAccess;
} public String getOperationId() {
return operationId;
} public void setOperationId(String operationId) {
this.operationId = operationId;
} public Integer getIsDelete() {
return isDelete;
} public void setIsDelete(Integer isDelete) {
this.isDelete = isDelete;
} public String getRouteUuid() {
return routeUuid;
} public void setRouteUuid(String routeUuid) {
this.routeUuid = routeUuid;
} public String getCurrentCatalogId() {
return currentCatalogId;
} public void setCurrentCatalogId(String currentCatalogId) {
this.currentCatalogId = currentCatalogId;
}
}

服务资源表

服务请求信息表:

@Data
@ApiModel(value = "服务请求信息表", description = "服务请求信息表")
public class ServiceRequest implements Serializable{ /**
* 程序序列化ID
*/
private static final long serialVersionUID=1L; /**
* 服务ID
*/ @ApiModelProperty(value = "服务ID")
private String id; /**
* 租户
*/ @ApiModelProperty(value = "租户")
private String gmtTenant; /**
* 创建时间
*/ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@ApiModelProperty(value = "创建时间")
private Date gmtCreate;
public Date getGmtCreate(){
return gmtCreate==null?null:(Date) gmtCreate.clone();
} public void setGmtCreate(Date gmtCreate){
this.gmtCreate = gmtCreate==null?null:(Date) gmtCreate.clone();
} /**
* 创建人
*/ @ApiModelProperty(value = "创建人")
private String gmtCreator; /**
* 创建人名称
*/ @ApiModelProperty(value = "创建人名称")
private String gmtCrtname; /**
* 最后修改时间
*/ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@ApiModelProperty(value = "最后修改时间")
private Date gmtModified;
public Date getGmtModified(){
return gmtModified==null?null:(Date) gmtModified.clone();
} public void setGmtModified(Date gmtModified){
this.gmtModified = gmtModified==null?null:(Date) gmtModified.clone();
} /**
* 最后修改人
*/ @ApiModelProperty(value = "最后修改人")
private String gmtModifiedby; /**
* 最后修改人名称
*/ @ApiModelProperty(value = "最后修改人名称")
private String gmtMfyname; /**
* 服务资源ID
*/ @ApiModelProperty(value = "服务资源ID")
private String serviceId; /**
* 参数名称
*/ @ApiModelProperty(value = "参数名称")
private String reqName; /**
* 参数描述
*/ @ApiModelProperty(value = "参数描述")
private String reqDesc; /**
* 参数类型
*/ @ApiModelProperty(value = "参数类型")
private String reqType; /**
* 参数长度
*/ @ApiModelProperty(value = "参数长度")
private Integer reqLength; /**
* 是否必填
*/ @ApiModelProperty(value = "是否必填")
private Integer isRequired; /**
* 参数来源
*/ @ApiModelProperty(value = "参数来源")
private String reqFrom; }

服务响应信息表:

@Data
@ApiModel(value = "服务响应信息表", description = "服务响应信息表")
public class ServiceResponse implements Serializable{ /**
* 程序序列化ID
*/
private static final long serialVersionUID=1L; /**
* 服务ID
*/ @ApiModelProperty(value = "服务ID")
private String id; /**
* 租户
*/ @ApiModelProperty(value = "租户")
private String gmtTenant; /**
* 创建时间
*/ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@ApiModelProperty(value = "创建时间")
private Date gmtCreate;
public Date getGmtCreate(){
return gmtCreate==null?null:(Date) gmtCreate.clone();
} public void setGmtCreate(Date gmtCreate){
this.gmtCreate = gmtCreate==null?null:(Date) gmtCreate.clone();
} /**
* 创建人
*/ @ApiModelProperty(value = "创建人")
private String gmtCreator; /**
* 创建人名称
*/ @ApiModelProperty(value = "创建人名称")
private String gmtCrtname; /**
* 最后修改时间
*/ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@ApiModelProperty(value = "最后修改时间")
private Date gmtModified;
public Date getGmtModified(){
return gmtModified==null?null:(Date) gmtModified.clone();
} public void setGmtModified(Date gmtModified){
this.gmtModified = gmtModified==null?null:(Date) gmtModified.clone();
} /**
* 最后修改人
*/ @ApiModelProperty(value = "最后修改人")
private String gmtModifiedby; /**
* 最后修改人名称
*/ @ApiModelProperty(value = "最后修改人名称")
private String gmtMfyname; /**
* 服务资源ID
*/ @ApiModelProperty(value = "服务资源ID")
private String serviceId; /**
* 属性名称
*/ @ApiModelProperty(value = "属性名称")
private String propName; /**
* 属性描述
*/ @ApiModelProperty(value = "属性描述")
private String propDesc; /**
* 属性类型
*/ @ApiModelProperty(value = "属性类型")
private String propType; /**
* 属性长度
*/ @ApiModelProperty(value = "属性长度")
private Integer propLength; }

服务响应状态表:

@Data
@ApiModel(value = "服务响应状态表", description = "服务响应状态表")
public class ResponseStatus implements Serializable{ /**
* 程序序列化ID
*/
private static final long serialVersionUID=1L; /**
* 服务ID
*/ @ApiModelProperty(value = "服务ID")
private String id; /**
* 租户
*/ @ApiModelProperty(value = "租户")
private String gmtTenant; /**
* 创建时间
*/ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@ApiModelProperty(value = "创建时间")
private Date gmtCreate;
public Date getGmtCreate(){
return gmtCreate==null?null:(Date) gmtCreate.clone();
} public void setGmtCreate(Date gmtCreate){
this.gmtCreate = gmtCreate==null?null:(Date) gmtCreate.clone();
} /**
* 创建人
*/ @ApiModelProperty(value = "创建人")
private String gmtCreator; /**
* 创建人名称
*/ @ApiModelProperty(value = "创建人名称")
private String gmtCrtname; /**
* 最后修改时间
*/ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@ApiModelProperty(value = "最后修改时间")
private Date gmtModified;
public Date getGmtModified(){
return gmtModified==null?null:(Date) gmtModified.clone();
} public void setGmtModified(Date gmtModified){
this.gmtModified = gmtModified==null?null:(Date) gmtModified.clone();
} /**
* 最后修改人
*/ @ApiModelProperty(value = "最后修改人")
private String gmtModifiedby; /**
* 最后修改人名称
*/ @ApiModelProperty(value = "最后修改人名称")
private String gmtMfyname; /**
* 服务资源ID
*/ @ApiModelProperty(value = "服务资源ID")
private String serviceId; /**
* 状态码
*/ @ApiModelProperty(value = "状态码")
private String statusCode; /**
* 状态描述
*/ @ApiModelProperty(value = "状态描述")
private String statusDesc; /**
* 状态说明
*/ @ApiModelProperty(value = "状态说明")
private String statusRemark;
}

服务类别表:

@ApiModel(value = "服务类别表", description = "服务类别表")
public class ServiceCatalog implements Serializable{ /**
* 程序序列化ID
*/
private static final long serialVersionUID=1L; /**
* 服务ID
*/ @ApiModelProperty(value = "服务ID")
private String id; /**
* 租户
*/ @ApiModelProperty(value = "租户")
private String gmtTenant; /**
* 创建时间
*/ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@ApiModelProperty(value = "创建时间")
private Date gmtCreate;
public Date getGmtCreate(){
return gmtCreate==null?null:(Date) gmtCreate.clone();
} public void setGmtCreate(Date gmtCreate){
this.gmtCreate = gmtCreate==null?null:(Date) gmtCreate.clone();
} /**
* 创建人
*/ @ApiModelProperty(value = "创建人")
private String gmtCreator; /**
* 创建人名称
*/ @ApiModelProperty(value = "创建人名称")
private String gmtCrtname; /**
* 最后修改时间
*/ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@ApiModelProperty(value = "最后修改时间")
private Date gmtModified;
public Date getGmtModified(){
return gmtModified==null?null:(Date) gmtModified.clone();
} public void setGmtModified(Date gmtModified){
this.gmtModified = gmtModified==null?null:(Date) gmtModified.clone();
} /**
* 最后修改人
*/ @ApiModelProperty(value = "最后修改人")
private String gmtModifiedby; /**
* 最后修改人名称
*/ @ApiModelProperty(value = "最后修改人名称")
private String gmtMfyname; /**
* 上级目录ID
*/ @ApiModelProperty(value = "上级目录ID")
private String pid; /**
* 目录名称
*/ @ApiModelProperty(value = "目录名称")
private String catalogName; public String getId() {
return id;
} public void setId(String id) {
this.id = id;
} public String getGmtTenant() {
return gmtTenant;
} public void setGmtTenant(String gmtTenant) {
this.gmtTenant = gmtTenant;
} public String getGmtCreator() {
return gmtCreator;
} public void setGmtCreator(String gmtCreator) {
this.gmtCreator = gmtCreator;
} public String getGmtCrtname() {
return gmtCrtname;
} public void setGmtCrtname(String gmtCrtname) {
this.gmtCrtname = gmtCrtname;
} public String getGmtModifiedby() {
return gmtModifiedby;
} public void setGmtModifiedby(String gmtModifiedby) {
this.gmtModifiedby = gmtModifiedby;
} public String getGmtMfyname() {
return gmtMfyname;
} public void setGmtMfyname(String gmtMfyname) {
this.gmtMfyname = gmtMfyname;
} public String getPid() {
return pid;
} public void setPid(String pid) {
this.pid = pid;
} public String getCatalogName() {
if(!StringUtils.isEmpty(catalogName)){
return catalogName.replaceAll(" ", "");
}
return catalogName;
} public void setCatalogName(String catalogName) {
this.catalogName = catalogName;
}
}

服务类别关系表:

@Data
@ApiModel(value = "服务类别关系表", description = "服务类别关系表")
public class ServiceCatalogRela implements Serializable{ /**
* 程序序列化ID
*/
private static final long serialVersionUID=1L; /**
* 服务ID
*/ @ApiModelProperty(value = "服务ID")
private String id; /**
* 租户
*/ @ApiModelProperty(value = "租户")
private String gmtTenant; /**
* 创建时间
*/ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@ApiModelProperty(value = "创建时间")
private Date gmtCreate;
public Date getGmtCreate(){
return gmtCreate==null?null:(Date) gmtCreate.clone();
} public void setGmtCreate(Date gmtCreate){
this.gmtCreate = gmtCreate==null?null:(Date) gmtCreate.clone();
} /**
* 创建人
*/ @ApiModelProperty(value = "创建人")
private String gmtCreator; /**
* 创建人名称
*/ @ApiModelProperty(value = "创建人名称")
private String gmtCrtname; /**
* 最后修改时间
*/ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@ApiModelProperty(value = "最后修改时间")
private Date gmtModified;
public Date getGmtModified(){
return gmtModified==null?null:(Date) gmtModified.clone();
} public void setGmtModified(Date gmtModified){
this.gmtModified = gmtModified==null?null:(Date) gmtModified.clone();
} /**
* 最后修改人
*/ @ApiModelProperty(value = "最后修改人")
private String gmtModifiedby; /**
* 最后修改人名称
*/ @ApiModelProperty(value = "最后修改人名称")
private String gmtMfyname; /**
* 目录ID
*/ @ApiModelProperty(value = "目录ID")
private String catalogId; /**
* 服务ID
*/ @ApiModelProperty(value = "服务ID")
private String serviceId; }

服务返回属性:

@Data
public class SwaggerModelAttr implements Serializable { private static final long serialVersionUID = -4074067438450613643L; /**
* 类名
*/
private String className = StringUtils.EMPTY;
/**
* 属性名
*/
private String name = StringUtils.EMPTY;
/**
* 类型
*/
private String type = StringUtils.EMPTY;
/**
* 属性描述
*/
private String description;
/**
* 嵌套属性列表
*/
private List<SwaggerModelAttr> properties = new ArrayList<>();
}

返回给前端的dto实体:

/**
* @program: share-capacity-platform
* @description: javabean转html 传递给前端的dto实体类
* @author: liumingyu
* @date: 2020-04-14 16:35
**/
@Data
public class SwaggerHtmlDto { /**
* 大标题
*/
private String title; /**
* 小标题
*/
private String tag; /**
* 版本
*/
private String version; /**
* 封装服务资源
*/
private ServiceResource serviceResource; /**
* 封装请求参数list
*/
private List<ServiceRequest> requestList; /**
* 封装响应状态码list
*/
private List<ResponseStatus> responseStatusList; /**
* 封装返回属性list
*/
private List<ServiceResponse> responseList;
}

实体类就是以上这些,将swagger json解析后存入相应的实体类字段中。

swagger解析代码:

下面开始swagger json的解析代码:

swagger解析service层接口:

public interface SwaggerJsonImportService {

    /**
* swaggerJson导入业务表
*
* @param jsonUrl jsonUrl
* @param serviceSwagger swaggerJson
* @param isAuthorized 是否需要授权访问
* @return net.evecom.scplatform.common.entry.CommonResp<java.lang.String>
* @throws IOException
* @Author Torres Liu
* @Description //TODO swaggerJson导入业务表
* @Date 2020/4/24 5:07 下午
* @Param [jsonUrl, serviceSwagger, isAuthorized]
**/
CommonResp<String> swaggerJsonImport(String jsonUrl, ServiceSwagger serviceSwagger, String isAuthorized) throws IOException; /**
* 导出SwaggerJson
*
* @param serviceId 服务id
* @param catalogId 目录id
* @return net.evecom.scplatform.common.entry.CommonResp<java.lang.String>
* @Author Torres Liu
* @Description //TODO 导出SwaggerJson
* @Date 2020/4/22 9:41 上午
* @Param [serviceId, catalogId]
**/
List<SwaggerHtmlDto> swaggerJsonExport(String serviceId, String catalogId);
}

swagger解析service层接口实现类(解析的核心代码)

下面是一大堆枯燥的json解析,大家都是程序员,我就不做过多的讲解代码,有需要学习的可以参照我代码中的注释,写的都比较详细。

package xxxxxxxx;

import cn.hutool.json.JSONObject;
import com.alibaba.fastjson.JSON; import lombok.extern.slf4j.Slf4j;
import net.evecom.scplatform.common.entry.CommonResp;
import net.evecom.scplatform.common.entry.system.CommonEntry;
import net.evecom.scplatform.common.entry.system.UserUtil;
import net.evecom.scplatform.common.utils.text.IDUtils;
import net.evecom.scplatform.openapi.dao.*;
import net.evecom.scplatform.openapi.entity.*;
import net.evecom.scplatform.openapi.entity.dto.SwaggerHtmlDto;
import net.evecom.scplatform.openapi.service.SwaggerJsonImportService;
import net.evecom.scplatform.openapi.util.SwaggerJsonUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.client.RestTemplate; import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.*; /**
* @author Torres Liu
* @description //TODO 服务导入/导出 业务层
* @date 2020-04-10 14:06 下午
**/
@SuppressWarnings({"unchecked", "rawtypes"})
@Slf4j
@Service
@Transactional(rollbackFor = Exception.class)
public class SwaggerJsonImportServiceImpl implements SwaggerJsonImportService { @Autowired
private RestTemplate restTemplate; @Autowired
private ServiceCatalogDao serviceCatalogDao; @Autowired
private ServiceResourceDao serviceResourceDao; @Autowired
private ServiceRequestDao serviceRequestDao; @Autowired
private ResponseStatusDao responseStatusDao; @Autowired
private ServiceResponseDao serviceResponseDao; @Autowired
private ServiceCatalogRelaDao serviceCatalogRelaDao; @Value("${kong.server-addr}")
private String kongServerAddr; /**
* array
*/
private static final String ARRAY_VAL = "array";
/**
* $ref
*/
private static final String REF_VAL = "$ref";
/**
* format
*/
private static final String FORMAT_VAL = "format";
/**
* schema
*/
private static final String SCHEMA_VAL = "schema";
/**
* 成功的code
*/
private static final int SUCCESS_CODE = 200;
/**
* 递归次数
*/
private static final int RECURSION_NUMS = 199; /**
* 通过JSON或URL导入服务
*
* @param jsonUrl
* @param serviceSwagger
* @param isAuthorized
* @return net.evecom.scplatform.common.entry.CommonResp<java.lang.String>
* @author Torres Liu
* @description //TODO 通过JSON或URL导入服务
* @date 2020/4/24 5:46 下午
**/
@Override
@Transactional(rollbackFor = Exception.class)
public CommonResp<String> swaggerJsonImport(String jsonUrl, ServiceSwagger serviceSwagger, String isAuthorized) throws IOException {
String jsonStr = "";
CloseableHttpClient httpClient = HttpClientBuilder.create().build();
//提前生成 一级标题的id字段 by liumingyu
String firstTitleUuid = IDUtils.new32UUID();
//获取前端传入的是否授权标识 by liumingyu
String isAuthorizedAccessStr = StringUtils.defaultIfBlank(isAuthorized, "0");
Integer isAuthorizedAccess = Integer.valueOf(isAuthorizedAccessStr);
try {
//判断是通过url or json传入数据 by liumingyu
if (!StringUtils.isBlank(jsonUrl) && "".equals(serviceSwagger.getSwaggerJson())) {
//判断url的有效性
boolean urlValidity = ifUrlValidity(jsonUrl);
//判断url是否是swagger的url
boolean swaggerUrl = ifSwaggerUrl(jsonUrl);
if (urlValidity && swaggerUrl) {
HttpGet httpGet = new HttpGet(jsonUrl);
CloseableHttpResponse response = httpClient.execute(httpGet);
if (response.getStatusLine().getStatusCode() == SUCCESS_CODE) {
HttpEntity entity = response.getEntity();
String string = EntityUtils.toString(entity, "utf-8");
jsonStr = string;
}
response.close();
httpClient.close();
} else {
return CommonResp.exception("传入的url不正确!");
}
} else if (serviceSwagger != null && !"".equals(serviceSwagger.getSwaggerJson())) {
String swaggerJson = serviceSwagger.getSwaggerJson();
//判断字符串是否为json格式
boolean isJson = isJson(swaggerJson);
if (isJson) {
JSONObject jsonObject = new JSONObject(swaggerJson);
Object o = JSON.toJSON(jsonObject);
jsonStr = com.alibaba.fastjson.JSONObject.toJSONString(o);
} else {
return CommonResp.exception("传入的json格式不正确!");
}
} else {
return CommonResp.exception("服务导入URL或JSON出错!");
}
//获取当前租户
String gmtTenant = UserUtil.getLoginUser().getGmtTenant();
//获取当前用户id
String userId = UserUtil.getLoginUser().getId();
String gmtCreator = (userId != null) ? userId : "";
//转换 JSON string to Map by liumingyu
Map<String, Object> map = SwaggerJsonUtils.readValue(jsonStr, HashMap.class);
//解析info by liumingyu
Map<String, Object> infoMap = (Map<String, Object>) map.get("info");
//拿到一级标题 by liumingyu
String catalogName = (String) infoMap.get("title");
//拿到所有二级标题(类标题)的List by liumingyu
List<Map<String, String>> tags = (List<Map<String, String>>) map.get("tags");
//如果表中没有该一级标题名称,需要新增一个 by liumingyu
if (catalogName != null) {
//查询库中是否有当前登录用户且pid为-1的根目录
ServiceCatalog catalogBeanByCreatorAndPid = serviceCatalogDao.findByCreatorAndPid(gmtCreator, "-1");
//查询库中是否存在该一级标题名称 by liumingyu
ServiceCatalog catalogBean = serviceCatalogDao.findByCatalogNameAndCreator(gmtCreator, catalogName);
//若不存在该标题名称,新增一级目录和二级目录 by liumingyu
//1.插入一级二级标题数据到服务目录表 by liumingyu
if (catalogBean == null && catalogBeanByCreatorAndPid != null) {
ServiceCatalog serviceCatalog = new ServiceCatalog();
serviceCatalog.setId(firstTitleUuid);
//将根目录id作为一级目录的pid
serviceCatalog.setPid(catalogBeanByCreatorAndPid.getId());
serviceCatalog.setCatalogName(catalogName);
serviceCatalogDao.add(serviceCatalog);
//添加二级标题(目录)by liumingyu
addTags(tags, firstTitleUuid, gmtTenant, gmtCreator);
} else {
//存在的话只要新增二级标题 by liumingyu
//拿到该一级目录的id by liumingyu
if (catalogBean != null && catalogBean.getId() != null) {
String fatherId = catalogBean.getId();
//添加二级标题(目录)by liumingyu
addTags(tags, fatherId, gmtTenant, gmtCreator);
}
}
}
//解析model by liumingyu
Map<String, SwaggerModelAttr> definitinMapOld = parseDefinitions(map);
Map<String, Map<String, Object>> definitinMap = newParseDefinitions(map);
//获取服务版本(取得是一级info的版本号)by liumingyu
String version = (String) infoMap.get("version");
//解析paths by liumingyu
Map<String, Map<String, Object>> paths = (Map<String, Map<String, Object>>) map.get("paths");
//解析bashPath by liumingyu
String basePath = (String) map.get("basePath");
String[] basePathSplit = null;
if (basePath != null && !"".equals(basePath)) {
basePathSplit = basePath.split(",");
}
if (paths != null) {
//通过entrySet()取出映射关系,iterator()迭代,存放到迭代器中 by liumingyu
Iterator<Map.Entry<String, Map<String, Object>>> it = paths.entrySet().iterator();
//开始遍历paths by liumingyu
while (it.hasNext()) {
//拿到单个path的数据信息,用map的Entry对象存起来 by liumingyu
Map.Entry<String, Map<String, Object>> path = it.next();
Iterator<Map.Entry<String, Object>> it2 = path.getValue().entrySet().iterator();
//请求url by liumingyu
String requestUrl = "";
if (basePathSplit.length > 0 && !"/".equals(basePathSplit[0])) {
//拼接 bashPath + url by liumingyu
requestUrl = kongServerAddr + basePathSplit[0] + path.getKey();
} else {
requestUrl = kongServerAddr + path.getKey();
}
while (it2.hasNext()) {
Map.Entry<String, Object> it2Request = it2.next();
//请求方法 GET / POST 等等 by liumingyu
String requestMethod = it2Request.getKey().toUpperCase();
//拿到某个接口(服务)的具体数据 by liumingyu
Map<String, Object> content = (Map<String, Object>) it2Request.getValue();
//服务名称 by liumingyu
String serviceName = String.valueOf(content.get("summary"));
//该服务的操作id by liumingyu
String operationId = String.valueOf(content.get("operationId"));
//请求体 by liumingyu
List<LinkedHashMap> parametersList = (ArrayList) content.get("parameters");
//响应Code体 by liumingyu
Map<String, Object> responsesList = (Map<String, Object>) content.get("responses");
//服务描述 by liumingyu
String serviceDesc = "";
String description = String.valueOf(content.get("description")); if (!"".equals(description) && description != null) {
serviceDesc = description;
}
//请求参数格式,类似于 multipart/form-data by liumingyu
String contentType = "";
List<String> consumes = (List) content.get("consumes");
if (consumes != null && consumes.size() > 0) {
contentType = StringUtils.join(consumes, ",");
}
//返回参数格式,类似于 application/json by liumingyu
String callContentType = "";
List<String> produces = (List) content.get("produces");
List<String> newProduces = new ArrayList<>();
for (String produce : produces) {
String newProduce = "";
if ("*/*".equals(produce) || "".equals(produce)) {
newProduce = "application/json";
} else {
newProduce = produce;
}
newProduces.add(newProduce);
}
if (newProduces != null && newProduces.size() > 0) {
callContentType = StringUtils.join(newProduces, ",");
}
//服务版本默认为1.0 by liumingyu
String serviceVersion = "1.0";
serviceVersion = StringUtils.defaultIfBlank(version, serviceVersion);
//查询当前库中是否存在operationId和服务名称,如果存在 后续所有数据不会进行添加 by liumingyu
List<ServiceResource> listByOperationId = serviceResourceDao.findByOperationId(operationId, serviceName, userId);
//若operationId不存在库中====>才进行后续的添加操作 by liumingyu
if (listByOperationId.size() == 0) {
//封装serviceResource表 by liumingyu
ServiceResource resourceTable = new ServiceResource();
//声明一个uuid 作为本轮遍历的resource表主键id,也是本轮遍历其他表对应的serviceId by liumingyu
String thisResourceId = IDUtils.new32UUID();
resourceTable.setId(thisResourceId);
resourceTable.setServiceName(serviceName);
resourceTable.setServiceDesc(serviceDesc);
resourceTable.setRequestUrl(requestUrl);
resourceTable.setRequestMethod(requestMethod);
resourceTable.setContentType(contentType);
resourceTable.setCallContentType(callContentType);
resourceTable.setServiceVersion(serviceVersion);
resourceTable.setIsValid(1);
resourceTable.setIsRelease(0);
resourceTable.setIsDelete(0);
resourceTable.setRouteUuid(UUID.randomUUID().toString());
//前端传入--->是否授权标识 by liumingyu
resourceTable.setIsAuthorizedAccess(isAuthorizedAccess);
//添加操作id by liumingyu
resourceTable.setOperationId(operationId);
//2.添加数据到serviceResource表 by liumingyu
serviceResourceDao.add(resourceTable); //处理parametersList数据转为ServiceRequest表List对象 by liumingyu
List<ServiceRequest> serviceRequestList = processRequestList(parametersList, definitinMap);
//3.添加数据到serviceRequest表 by liumingyu
addServiceRequest(serviceRequestList, thisResourceId, gmtTenant); //处理responsesList数据转为ResponseStatus表List对象 by liumingyu
List<ResponseStatus> responseStatusList = processResponseStatusList(responsesList, definitinMap);
//4.添加数据到ResponseStatus表 by liumingyu
addResponseStatus(responseStatusList, thisResourceId, gmtTenant); //取出来状态是200时的返回值 by liumingyu
Map<String, Object> responsesObj = (Map<String, Object>) responsesList.get("200");
if (responsesObj != null && responsesObj.get(SCHEMA_VAL) != null) {
//处理相应的返回值 by liumingyu
SwaggerModelAttr swaggerModelAttr = processResponseModelAttrs(responsesObj, definitinMapOld);
//拿到properties数据,这个List里面就是需要的返回值数据 by liumingyu
List<SwaggerModelAttr> propertiesList = swaggerModelAttr.getProperties();
//5.添加数据到ServiceResponse表(传递propertiesList和主表的id) by liumingyu
addServiceResponse(propertiesList, thisResourceId, gmtTenant);
} //操作服务类别关系表(目录和服务关系表) by liumingyu
String tagsName = String.valueOf(((List) content.get("tags")).get(0));
//6.添加数据到目录关系表 by liumingyu
addCatalogRela(tagsName, thisResourceId, gmtCreator);
} else {
log.info("迭代器当前执行到的对象operationId「" + operationId + "」已存在数据库中,不进行插入");
}
}
}
}
} catch (Exception e) {
log.error("服务导入失败", e);
return CommonResp.exception("服务导入失败");
}
return CommonResp.succeed("服务导入成功!");
} /**
* 服务导出Json
*
* @param serviceId
* @param catalogId
* @return java.util.List<net.evecom.scplatform.openapi.entity.dto.SwaggerHtmlDto>
* @author Torres Liu
* @description //TODO 服务导出Json
* @date 2020/4/24 5:50 下午
**/
@Override
public List<SwaggerHtmlDto> swaggerJsonExport(String serviceId, String catalogId) {
String titleName = "";
List<SwaggerHtmlDto> result = new ArrayList<>();
try {
if (serviceId != null && !"".equals(serviceId)) {
SwaggerHtmlDto thisDto = new SwaggerHtmlDto();
//根据serviceId查询所需数据
ServiceResource resourceTable = serviceResourceDao.findById(serviceId);
List<ServiceRequest> requestList = serviceRequestDao.findByServiceId(serviceId);
List<ResponseStatus> responseStatusList = responseStatusDao.findByServiceId(serviceId);
List<ServiceResponse> serviceResponseList = serviceResponseDao.findByServiceId(serviceId);
ServiceCatalog thisCatalogBean = serviceCatalogDao.findById(catalogId == null ? "" : catalogId); //将数据set到自定义封装的dto实体中
thisDto.setServiceResource(resourceTable);
thisDto.setRequestList(requestList);
thisDto.setResponseStatusList(responseStatusList);
thisDto.setResponseList(serviceResponseList);
//声明一个标题名 titleName = resourceTable.getServiceName();
if (thisCatalogBean != null) {
thisDto.setTag(thisCatalogBean.getCatalogName());
titleName = thisCatalogBean.getCatalogName() + "-" + titleName;
String thisCatalogPid = thisCatalogBean.getPid();
ServiceCatalog pidBean = serviceCatalogDao.findById(thisCatalogPid);
if (pidBean != null) {
thisDto.setTitle(pidBean.getCatalogName());
titleName = pidBean.getCatalogName() + "-" + titleName;
}
}
thisDto.setVersion(resourceTable.getServiceVersion());
//将所有数据add至result
result.add(thisDto);
return result;
}
} catch (Exception e) {
log.error("服务导出异常:", e);
}
return result;
} /**
* @param tags
* @param pid
* @param gmtTenant
* @return void
* @author Torres Liu
* @description //TODO 将json的二级标题,新增至目录表作为二级目录,pid为一级目录id
* @date 2020/4/24 5:52 下午
**/
private void addTags(List<Map<String, String>> tags, String pid, String gmtTenant, String gmtCreator) {
if (tags != null) {
gmtTenant = (gmtTenant != null) ? gmtTenant : "";
List<ServiceCatalog> catalogList = new ArrayList<>();
for (Map tag : tags) {
String name = (String) tag.get("name");
if (name != null && !"".equals(name)) {
ServiceCatalog catalogBean2 = serviceCatalogDao.findByCatalogNameAndCreator(gmtCreator, name);
//如果没有该二级目录则开始新增二级目录 by liumingyu
if (catalogBean2 == null && pid != null) {
ServiceCatalog serviceCatalogBean = new ServiceCatalog();
serviceCatalogBean.setPid(pid);
serviceCatalogBean.setCatalogName(name);
serviceCatalogBean.setId(IDUtils.new32UUID());
catalogList.add(serviceCatalogBean);
}
}
}
if (catalogList.size() > 0) {
//传入List批量添加
serviceCatalogDao.batchAdd(catalogList, new CommonEntry(), gmtTenant);
} }
} /**
* @param serviceRequestList
* @param thisResourceId
* @param gmtTenant
* @return void
* @author Torres Liu
* @description //TODO 添加数据到 服务请求表
* @date 2020/4/24 5:52 下午
**/
private void addServiceRequest(List<ServiceRequest> serviceRequestList, String thisResourceId, String gmtTenant) {
List<ServiceRequest> requestList = new ArrayList<>();
if (serviceRequestList != null && thisResourceId != null) {
gmtTenant = (gmtTenant != null) ? gmtTenant : "";
for (ServiceRequest requestParameter : serviceRequestList) {
ServiceRequest requestTable = new ServiceRequest();
requestTable.setId(IDUtils.new32UUID());
requestTable.setServiceId(thisResourceId);
requestTable.setReqName(requestParameter.getReqName());
requestTable.setReqDesc(requestParameter.getReqDesc());
requestTable.setIsRequired(requestParameter.getIsRequired());
requestTable.setReqType(requestParameter.getReqType());
requestTable.setReqFrom(requestParameter.getReqFrom());
requestList.add(requestTable);
}
if (requestList.size() > 0) {
//批量添加数据
serviceRequestDao.batchAdd(requestList, new CommonEntry(), gmtTenant);
}
}
} /**
* @param responseStatusList
* @param thisResourceId
* @param gmtTenant
* @return void
* @author Torres Liu
* @description //TODO 添加数据到 响应状态表
* @date 2020/4/24 5:54 下午
**/
private void addResponseStatus(List<ResponseStatus> responseStatusList, String thisResourceId, String gmtTenant) {
List<ResponseStatus> statusList = new ArrayList<>();
if (responseStatusList != null && thisResourceId != null) {
gmtTenant = (gmtTenant != null) ? gmtTenant : "";
for (ResponseStatus response : responseStatusList) {
ResponseStatus responseStatusTable = new ResponseStatus();
responseStatusTable.setId(IDUtils.new32UUID());
responseStatusTable.setServiceId(thisResourceId);
responseStatusTable.setStatusCode(response.getStatusCode());
responseStatusTable.setStatusDesc(response.getStatusDesc());
responseStatusTable.setStatusRemark(response.getStatusRemark());
statusList.add(responseStatusTable);
}
if (statusList.size() > 0) {
//批量添加数据
responseStatusDao.batchAdd(statusList, new CommonEntry(), gmtTenant);
}
}
} /**
* @param propertiesList
* @param thisResourceId
* @param gmtTenant
* @return void
* @author Torres Liu
* @description //TODO 添加数据到 服务响应表
* @date 2020/4/24 5:55 下午
**/
private void addServiceResponse(List<SwaggerModelAttr> propertiesList, String thisResourceId, String gmtTenant) {
List<ServiceResponse> responseList = new ArrayList<>();
if (propertiesList != null && thisResourceId != null) {
gmtTenant = (gmtTenant != null) ? gmtTenant : "";
//添加数据到serviceResponse表 by liumingyu
for (SwaggerModelAttr p : propertiesList) {
ServiceResponse serviceResponseTable = new ServiceResponse();
serviceResponseTable.setId(IDUtils.new32UUID());
//该服务id 为前面生成的serviceResource表(主表)的主键(资源id)by liumingyu
serviceResponseTable.setServiceId(thisResourceId);
serviceResponseTable.setPropName(p.getName());
serviceResponseTable.setPropType(p.getType());
serviceResponseTable.setPropDesc(p.getDescription());
responseList.add(serviceResponseTable);
}
if (responseList.size() > 0) {
//批量添加数据
serviceResponseDao.batchAdd(responseList, new CommonEntry(), gmtTenant);
}
}
} /**
* @param tagsName
* @param thisResourceId
* @return void
* @author Torres Liu
* @description //TODO 添加数据到目录关系表
* @date 2020/4/24 5:55 下午
**/
private void addCatalogRela(String tagsName, String thisResourceId, String gmtCreator) {
ServiceCatalogRela catalogRelaTable = new ServiceCatalogRela();
if (tagsName != null && !"".equals(tagsName) && thisResourceId != null) {
//通过tagsName查出目录id by liumingyu
ServiceCatalog catalogBean = serviceCatalogDao.findByCatalogNameAndCreator(gmtCreator, tagsName);
if (catalogBean != null && catalogBean.getId() != null) {
String catalogId = catalogBean.getId();
catalogRelaTable.setId(IDUtils.new32UUID());
//将目录id插入关系表 by liumingyu
catalogRelaTable.setCatalogId(catalogId);
//将当前的服务id插入关系表 by liumingyu
catalogRelaTable.setServiceId(thisResourceId);
serviceCatalogRelaDao.add(catalogRelaTable);
}
}
} /**
* @param map
* @return java.util.Map<java.lang.String, net.evecom.scplatform.openapi.entity.SwaggerModelAttr>
* @author Torres Liu
* @description //TODO 解析Definitions
* @date 2020/4/24 5:55 下午
**/
private Map<String, SwaggerModelAttr> parseDefinitions(Map<String, Object> map) {
Map<String, Map<String, Object>> definitions = (Map<String, Map<String, Object>>) map.get("definitions");
Map<String, SwaggerModelAttr> definitinMap = new HashMap<>(256);
if (definitions != null) {
Iterator<String> modelNameIt = definitions.keySet().iterator();
while (modelNameIt.hasNext()) {
String modeName = modelNameIt.next();
Map<String, Object> modeProperties = (Map<String, Object>) definitions.get(modeName).get("properties");
if (modeProperties == null) {
continue;
}
Iterator<Map.Entry<String, Object>> mIt = modeProperties.entrySet().iterator(); List<SwaggerModelAttr> attrList = new ArrayList<>(); //解析属性 by liumingyu
while (mIt.hasNext()) {
Map.Entry<String, Object> mEntry = mIt.next();
Map<String, Object> attrInfoMap = (Map<String, Object>) mEntry.getValue();
SwaggerModelAttr modeAttr = new SwaggerModelAttr();
modeAttr.setName(mEntry.getKey());
modeAttr.setType((String) attrInfoMap.get("type"));
if (attrInfoMap.get(FORMAT_VAL) != null) {
modeAttr.setType(modeAttr.getType() + "(" + attrInfoMap.get("format") + ")");
}
modeAttr.setType(StringUtils.defaultIfBlank(modeAttr.getType(), "object"));
modeAttr.setDescription((String) attrInfoMap.get("description"));
attrList.add(modeAttr);
} SwaggerModelAttr modeAttr = new SwaggerModelAttr();
Object title = definitions.get(modeName).get("title");
Object description = definitions.get(modeName).get("description");
modeAttr.setClassName(title == null ? "" : title.toString());
modeAttr.setDescription(description == null ? "" : description.toString());
modeAttr.setProperties(attrList);
definitinMap.put("#/definitions/" + modeName, modeAttr);
}
}
return definitinMap;
} /**
* @param map
* @return java.util.Map<java.lang.String, java.util.Map < java.lang.String, java.lang.Object>>
* @author Torres Liu
* @description //TODO 解析Definitions---->new
* @date 2020/4/24 5:55 下午
**/
private Map<String, Map<String, Object>> newParseDefinitions(Map<String, Object> map) {
Map<String, Map<String, Object>> definitions = (Map<String, Map<String, Object>>) map.get("definitions");
Map<String, Map<String, Object>> definitinMap = new HashMap<>(256);
if (definitions != null) {
Iterator<String> modelNameIt = definitions.keySet().iterator();
while (modelNameIt.hasNext()) {
String modeName = modelNameIt.next();
Map<String, Object> modeProperties = definitions.get(modeName); definitinMap.put("#/definitions/" + modeName, modeProperties);
}
}
return definitinMap;
} /**
* @param parameters
* @param definitionMap
* @return java.util.List<net.evecom.scplatform.openapi.entity.ServiceRequest>
* @author Torres Liu
* @description //TODO 处理请求List
* @date 2020/4/24 5:56 下午
**/
private List<ServiceRequest> processRequestList(List<LinkedHashMap> parameters, Map<String, Map<String, Object>> definitionMap) {
List<ServiceRequest> requestList = new ArrayList<>();
Map<String, Object> myHashMap = new HashMap<>(2000);
if (!CollectionUtils.isEmpty(parameters)) {
for (Map<String, Object> param : parameters) {
Object in = param.get("in");
ServiceRequest request = new ServiceRequest();
request.setReqName(String.valueOf(param.get("name")));
request.setReqType(param.get("type") == null ? "object" : param.get("type").toString());
request.setReqFrom(String.valueOf(in));
// 考虑对象参数类型 by liumingyu
if (in != null && "body".equals(in)) {
Map<String, Object> schema = (Map) param.get("schema");
//拿到 ----> #/definitions/文件目录请求对象
Object ref = schema.get("$ref");
if (ref != null) {
Map<String, Object> mapByRefValue = definitionMap.get(ref);
if (mapByRefValue != null) {
Map<String, Object> propertiesMap = (Map<String, Object>) mapByRefValue.get("properties");
if (propertiesMap != null) {
//将properties中的值进行迭代
Iterator<Map.Entry<String, Object>> itProp = propertiesMap.entrySet().iterator();
while (itProp.hasNext()) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dataFormat = simpleDateFormat.format(new Date());
//取到单个值 ----> 如 endRow: {type: "integer", format: "int32"}
Map.Entry<String, Object> entryIt = itProp.next();
Map<String, Object> itValue = (Map<String, Object>) entryIt.getValue();
String type = (String) itValue.get("type");
if (!StringUtils.isBlank(type)) {
switch (type) {
case "string":
if (itValue.get(FORMAT_VAL) != null && !"".equals(itValue.get(FORMAT_VAL))) {
String format = (String) itValue.get("format");
if ("date-time".equals(format) || "dateTime".equals(format)) {
myHashMap.put(entryIt.getKey(), dataFormat);
}
} else {
myHashMap.put(entryIt.getKey(), "string");
}
break;
case "integer":
myHashMap.put(entryIt.getKey(), 0);
break;
case "number":
myHashMap.put(entryIt.getKey(), 0.0);
break;
case "boolean":
myHashMap.put(entryIt.getKey(), true);
break;
case "array":
Integer initNum = 0;
//开始调用--->递归算法逻辑
ifArrayRecursion(itValue, definitionMap, dataFormat, myHashMap, entryIt, initNum);
break;
default:
myHashMap.put(entryIt.getKey(), null);
break;
}
}
}
}
}
}
request.setReqDesc(JSON.toJSONString(myHashMap));
} else {
request.setReqDesc((String.valueOf(param.get("description"))));
}
// 是否必填 by liumingyu
request.setIsRequired(0);
if (param.get("required") != null) {
Boolean required = (Boolean) param.get("required");
if (required == true) {
request.setIsRequired(1);
}
}
requestList.add(request);
}
}
return requestList;
} /**
* @param responsesList
* @param definitionMap
* @return java.util.List<net.evecom.scplatform.openapi.entity.ResponseStatus>
* @author Torres Liu
* @description //TODO 处理返回状态码CodeList(像200、404、401...)
* @date 2020/4/24 5:56 下午
**/
private List<ResponseStatus> processResponseStatusList(Map<String, Object> responsesList, Map<String, Map<String, Object>> definitionMap) {
List<ResponseStatus> responseStatusList = new ArrayList<>();
Iterator<Map.Entry<String, Object>> resIt = responsesList.entrySet().iterator();
while (resIt.hasNext()) {
Map.Entry<String, Object> entry = resIt.next();
//声明个响应状态码实体
ResponseStatus responseStatus = new ResponseStatus();
//开始迭代 状态码200 201 401 403 404 等等 by liumingyu
responseStatus.setStatusCode(entry.getKey());
//获取response的value 像---> {description: "OK", schema: {$ref: "#/definitions/CommonResp«string»"}}
LinkedHashMap<String, Object> statusCodeInfo = (LinkedHashMap) entry.getValue();
//setDescription
responseStatus.setStatusDesc(String.valueOf(statusCodeInfo.get("description")));
if ("200".equals(entry.getKey())) {
Object schema = statusCodeInfo.get("schema");
if (schema != null && ((LinkedHashMap) schema).get("$ref") != null) {
//定义一个存储definition的map
Map<String, Object> myHashMap = new HashMap<>(2000);
Map<String, Object> myHashMap2 = new HashMap<>(2000);
//如果schema不为null,开始解析$ref ---> $ref: "#/definitions/CommonResp«string»"
Object ref = ((LinkedHashMap) schema).get("$ref");
//将取到的ref的值放入definitionMap作为key去查询该definitions的具体内容
Map<String, Object> mapByRef1 = definitionMap.get(ref);
//获取到该definitions中的properties字段内容(里面是code、data、msg)
Map<String, Object> properties = (Map<String, Object>) mapByRef1.get("properties");
//将properties拿来迭代,继续后续逻辑...
Iterator<Map.Entry<String, Object>> itProperties = properties.entrySet().iterator();
while (itProperties.hasNext()) {
//拿到entry对象,其中entryProp的key 是 code、data、msg
Map.Entry<String, Object> entryProp = itProperties.next();
//取到entryProp的value
Map<String, Object> valueMap = (Map<String, Object>) entryProp.getValue();
//其中如果是data,那可能里面还存在$ref
if (valueMap.get("$ref") != null || valueMap.get("items") != null) {
//如果存在 继续取出$ref的值 ---> 如 #/definitions/PageInfo«ScFile对象»
Object refValue = valueMap.get("$ref");
Map<String, Object> thisItems = (Map<String, Object>) valueMap.get("items");
Object refValues = (refValue != null) ? refValue : thisItems.get("$ref");
//继续将refValue作为key通过definitionMap来获取definitions
Map<String, Object> mapByRefValue = definitionMap.get(refValues);
if (mapByRefValue != null) {
//继续获取该definitions中的properties的值
Map<String, Object> propertiesMap = (Map<String, Object>) mapByRefValue.get("properties");
if (propertiesMap != null) {
//将properties中的值进行迭代
Iterator<Map.Entry<String, Object>> itProp = propertiesMap.entrySet().iterator();
while (itProp.hasNext()) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dataFormat = simpleDateFormat.format(new Date());
//取到单个值 ----> 如 endRow: {type: "integer", format: "int32"}
Map.Entry<String, Object> entryIt = itProp.next();
// entryIt.getKey()= endRow ; entryIt.getValue() = {type: "integer", format: "int32"}
Map<String, Object> itValue = (Map<String, Object>) entryIt.getValue();
String type = (String) itValue.get("type");
if (!StringUtils.isBlank(type)) {
switch (type) {
case "string":
if (itValue.get(FORMAT_VAL) != null && !"".equals(itValue.get(FORMAT_VAL))) {
String format = (String) itValue.get("format");
if ("date-time".equals(format) || "dateTime".equals(format)) {
myHashMap2.put(entryIt.getKey(), dataFormat);
}
} else {
myHashMap2.put(entryIt.getKey(), "string");
}
break;
case "integer":
myHashMap2.put(entryIt.getKey(), 0);
break;
case "number":
myHashMap2.put(entryIt.getKey(), 0.0);
break;
case "boolean":
myHashMap2.put(entryIt.getKey(), true);
break;
case "array":
Integer initNum = 0;
//开始调用--->递归算法逻辑
ifArrayRecursion(itValue, definitionMap, dataFormat, myHashMap2, entryIt, initNum);
break;
default:
myHashMap2.put(entryIt.getKey(), null);
break;
}
}
}
}
}
//将myHashMap2存入data
myHashMap.put(entryProp.getKey(), myHashMap2);
} else {
//不存在ref的直接存入map中
if (valueMap.get("type") != null) {
//拿到type的值
String type = (String) valueMap.get("type");
switch (type) {
case "string":
myHashMap.put(entryProp.getKey(), "string");
break;
case "integer":
myHashMap.put(entryProp.getKey(), 0);
break;
case "number":
myHashMap.put(entryProp.getKey(), 0.0);
break;
case "boolean":
myHashMap.put(entryProp.getKey(), true);
break;
default:
myHashMap.put(entryProp.getKey(), new Object());
break;
}
}
}
}
responseStatus.setStatusRemark(JSON.toJSONString(myHashMap));
}
}
responseStatusList.add(responseStatus);
}
return responseStatusList;
} /**
* @return net.evecom.scplatform.openapi.entity.SwaggerModelAttr
* @Author liumingyu
* @Description //TODO 处理返回属性列表
* @Date 2020/4/8 6:56 下午
* @Param [responseObj, definitinMap]
**/
private SwaggerModelAttr processResponseModelAttrs(Map<String, Object> responseObj, Map<String, SwaggerModelAttr> definitinMap) {
Map<String, Object> schema = (Map<String, Object>) responseObj.get("schema");
String type = (String) schema.get("type");
String ref = null;
//数组 by liumingyu
if (ARRAY_VAL.equals(type)) {
Map<String, Object> items = (Map<String, Object>) schema.get("items");
if (items != null && items.get(REF_VAL) != null) {
ref = (String) items.get(REF_VAL);
}
}
//对象 by liumingyu
if (schema.get(REF_VAL) != null) {
ref = (String) schema.get(REF_VAL);
} //其他类型 by liumingyu
SwaggerModelAttr modelAttr = new SwaggerModelAttr();
modelAttr.setType(StringUtils.defaultIfBlank(type, StringUtils.EMPTY)); if (StringUtils.isNotBlank(ref) && definitinMap.get(ref) != null) {
modelAttr = definitinMap.get(ref);
}
return modelAttr;
} /**
* @param itValue
* @param definitionMap
* @param dataFormat
* @param myHashMapPre
* @param entryPreIt
* @param initNums
* @return void
* @author Torres Liu
* @description //TODO ifArray递归算法 [如果参数存在type=array,开始执行该递归逻辑,该递归是为了解析json中多层嵌套array的数据]
* @date 2020/4/24 5:56 下午
**/
private void ifArrayRecursion(Map<String, Object> itValue, Map<String, Map<String, Object>> definitionMap,
String dataFormat, Map<String, Object> myHashMapPre,
Map.Entry<String, Object> entryPreIt, Integer initNums) {
if (initNums < RECURSION_NUMS) {
Map<String, Object> newHashMap = new HashMap<>(128);
ArrayList<Map<String, Object>> newArrayListMap1 = new ArrayList<>();
ArrayList newArrayListMap2 = new ArrayList();
//如果为array类型,说明可能还嵌套一层的数据
Map<String, Object> items = (Map<String, Object>) itValue.get("items");
if (items.get(REF_VAL) != null || itValue.get(REF_VAL) != null) {
//获取到items中$ref的值
String itemsRef = (String) items.get("$ref");
Object itemsRefs;
itemsRefs = (itemsRef != null) ? itemsRef : itValue.get(REF_VAL);
//通过definitionMap获取到itemsRef对应的definitions
Map<String, Object> itemsRefMap = definitionMap.get(itemsRefs);
//继续获取properties中的数据
Map<String, Object> itemsPropertiesMap = (Map<String, Object>) itemsRefMap.get("properties");
//声明迭代器
if (itemsPropertiesMap != null) {
Iterator<Map.Entry<String, Object>> itemsIterator = itemsPropertiesMap.entrySet().iterator();
while (itemsIterator.hasNext()) {
//拿到具体对象
Map.Entry<String, Object> itemsIt = itemsIterator.next();
Map<String, Object> itemsItValue = (Map<String, Object>) itemsIt.getValue();
//取到type来为后续类型做判断
String itemsType = (String) itemsItValue.get("type");
if (!StringUtils.isBlank(itemsType)) {
switch (itemsType) {
case "string":
if (itemsItValue.get("format") != null && !"".equals(itemsItValue.get("format"))) {
String itemsFormat = (String) itemsItValue.get("format");
if ("date-time".equals(itemsFormat) || "dateTime".equals(itemsFormat)) {
newHashMap.put(itemsIt.getKey(), dataFormat);
}
} else {
newHashMap.put(itemsIt.getKey(), "string");
}
break;
case "integer":
newHashMap.put(itemsIt.getKey(), 0);
break;
case "number":
newHashMap.put(itemsIt.getKey(), 0.0);
break;
case "boolean":
newHashMap.put(itemsIt.getKey(), true);
break;
case "array":
Integer nums = initNums + 1;
ifArrayRecursion(itemsItValue, definitionMap, dataFormat, newHashMap, itemsIt, nums);
default:
newHashMap.put(itemsIt.getKey(), new Object());
break;
}
}
}
}
newArrayListMap1.add(newHashMap);
myHashMapPre.put(entryPreIt.getKey(), newArrayListMap1);
} else {
//没有ref的也要去解析array
String typeArray = (String) items.get("type");
switch (typeArray) {
case "string":
newArrayListMap2.add("string");
break;
case "integer":
newArrayListMap2.add(0);
break;
case "number":
newArrayListMap2.add(0.0);
break;
case "boolean":
newArrayListMap2.add(true);
break;
default:
newArrayListMap2.add(new Object());
break;
}
myHashMapPre.put(entryPreIt.getKey(), newArrayListMap2);
}
} else {
log.info("当前对象递归次数超过199次!!!");
System.out.println("当前对象递归次数超过199次!!!");
}
} /**
* 判断是否为有效url
*
* @param urlStr
* @return boolean
* @author Torres Liu
* @description //TODO 判断是否为有效url
* @date 2020/4/29 5:08 下午
**/
private boolean ifUrlValidity(String urlStr) {
URL url;
HttpURLConnection con;
int state = -1;
try {
url = new URL(urlStr);
con = (HttpURLConnection) url.openConnection();
state = con.getResponseCode();
if (state != 200) {
return false;
}
} catch (Exception e1) {
return false;
}
return true;
} /**
* 判断是否为swaggerUrl
*
* @param urlStr
* @return boolean
* @author Torres Liu
* @description //TODO 判断是否为swaggerUrl
* @date 2020/4/29 5:09 下午
**/
private boolean ifSwaggerUrl(String urlStr) {
boolean contains = urlStr.contains("api-docs");
return contains;
} /**
* 判断字符串是否为json格式
*
* @param jsonStr
* @return boolean
* @author Torres Liu
* @description //TODO 判断字符串是否为json格式
* @date 2020/4/29 5:09 下午
**/
private boolean isJson(String jsonStr) {
try {
JSONObject jsonObject = new JSONObject(jsonStr);
if (jsonObject.toString() == null) {
return false;
}
return true;
} catch (Exception e) {
return false;
}
} }

swagger解析controller层代码:

/**
* @program: share-capacity-platform
* @description: 导入服务
* @author: Torres Liu
* @date: 2020-04-14 10:33
**/
@RestController
@RequestMapping("/swaggerJsonImport")
public class SwaggerJsonImportController { @Autowired
private SwaggerJsonImportService swaggerJsonImportService; /**
* swaggerUrl 或 swaggerJson导入服务
*
* @param url jsonUrl
* @param isAuthorized 是否需要授权
* @param serviceSwagger swaggerJson
* @return net.evecom.scplatform.common.entry.CommonResp<java.lang.String>
* @author Torres Liu
* @description //TODO swaggerUrl 或 swaggerJson导入服务
* @date 2020/4/28 5:52 下午
**/
@PostMapping("/importService")
public CommonResp<String> swaggerImport(@RequestParam(value = "url", required = false) String url,
@RequestParam(value = "isAuthorized") String isAuthorized,
@RequestBody(required = false) ServiceSwagger serviceSwagger) throws IOException {
CommonResp<String> responseString = swaggerJsonImportService.swaggerJsonImport(url, serviceSwagger, isAuthorized);
return responseString;
} /**
* 导出Json功能
*
* @param serviceId 服务id
* @param catalogId 目录id
* @return java.util.List<net.evecom.scplatform.openapi.entity.dto.SwaggerHtmlDto>
* @author Torres Liu
* @description //TODO 导出Json功能
* @date 2020/4/28 5:53 下午
**/
@GetMapping("/swaggerJsonExport")
public List<SwaggerHtmlDto> swaggerJsonExport(@RequestParam("serviceId") String serviceId,
@RequestParam(value = "catalogId", required = false) String catalogId) {
List<SwaggerHtmlDto> swaggerHtmlDtoList = swaggerJsonImportService.swaggerJsonExport(serviceId, catalogId);
return swaggerHtmlDtoList;
}
}

JavaBean转为html渲染页面代码:

渲染html的controller层代码:

/**
* @program: share-capacity-platform
* @description: javabean转swagger html详情页
* @author: Torres Liu
* @date: 2020-04-14 22:21
**/
@Controller
@RequestMapping("/beanToSwaggerHtml")
public class BeanToSwaggerHtmlController { @Autowired
private RestTemplate restTemplate; @Autowired
private SwaggerToHtmlByBeanServiceImpl swaggerToHtmlByBeanService; @Value("${spring.application.name}")
private String appName; @Value("${server.port}")
private String port; /**
* word方式标识
**/
private String word = "word";
/**
* excel方式标识
**/
private String excel = "excel"; /**
* @return java.lang.String
* @Author Torres Liu
* @Description //TODO bean转api风格的html
* @Date 2020/4/15 12:49 上午
* @Param [model, serviceId, catalogId]
**/
@GetMapping("/beanToHtml")
public String getBeanToHtml(Model model, @RequestParam("serviceId") String serviceId,
@RequestParam(value = "catalogId", required = false) String catalogId,
@RequestParam(value = "download", required = false, defaultValue = "1") Integer download) throws SocketException, UnknownHostException {
Map<String, Object> result = swaggerToHtmlByBeanService.getBeanToHtml(serviceId, catalogId);
model.addAllAttributes(result);
model.addAttribute("download", download);
model.addAttribute("serviceId", serviceId);
model.addAttribute("catalogId", catalogId);
//获取当前IP
String ipAddr = WebToolUtils.getLocalIp();
model.addAttribute("ipAndPort", "http://192.168.66.40:50092/" + appName);
System.out.println("[IP] =====> "+ ipAddr);
return "beanToHtmlTemplate";
} /**
* @return void
* @Author Torres Liu
* @Description //TODO 将html导出word和excel
* @Date 2020/4/15 11:21 上午
* @Param [serviceId, outputType, catalogId, response]
**/
@RequestMapping("/downloadWordByBean")
public void downloadWord(@RequestParam("serviceId") String serviceId, @RequestParam("outputType") String outputType,
@RequestParam("catalogId") String catalogId, HttpServletResponse response) {
ResponseEntity<String> forEntity = restTemplate.getForEntity("http://" + appName + ":" + "/beanToSwaggerHtml/beanToHtml?download=0&serviceId=" + serviceId + "&catalogId=" + catalogId, String.class);
response.setContentType("application/octet-stream;charset=utf-8");
response.setCharacterEncoding("utf-8");
try (BufferedOutputStream bos = new BufferedOutputStream(response.getOutputStream())) {
if (word.equals(outputType)) {
response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode("WordApi_" + System.currentTimeMillis() + ".doc", "utf-8"));
} else if (excel.equals(outputType)) {
response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode("ExcelApi_" + System.currentTimeMillis() + ".xlsx", "utf-8"));
}
byte[] bytes = forEntity.getBody().getBytes("utf-8");
bos.write(bytes, 0, bytes.length);
bos.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}

渲染html的service接口:

public interface SwaggerBeanToHtmlService {

    /**
* @param serviceId
* @param catalogId
* @return java.util.Map<java.lang.String, java.lang.Object>
* @Author Torres Liu
* @Description //TODO 通过serviceId获取相应javabean转为html
* @Date 2020/4/14 4:33 下午
**/
Map<String, Object> getBeanToHtml(String serviceId, String catalogId);
}

渲染html的service实现类:

/**
* @Author Torres Liu
* @Description //TODO swagger-json转html和word格式具体实现 (解析swagger-json)
* @Date 2020/4/8 3:52 下午
* @Param
* @return
**/
@SuppressWarnings({"unchecked", "rawtypes"})
@Slf4j
@Service
public class SwaggerToHtmlByBeanServiceImpl implements SwaggerBeanToHtmlService { @Autowired
private ServiceResourceDao serviceResourceDao; @Autowired
private ServiceRequestDao serviceRequestDao; @Autowired
private ServiceResponseDao serviceResponseDao; @Autowired
private ResponseStatusDao responseStatusDao; @Autowired
private ServiceCatalogDao serviceCatalogDao; /**
* 顶级目录的pid
*/
private static final String MAX_CATALOG_PID = "-1"; /**
* @param serviceId
* @param catalogId
* @return java.util.Map<java.lang.String, java.lang.Object>
* @author Torres Liu
* @description //TODO 解析===>通过serviceId获取相应javabean转为html
* @date 2020/4/24 6:01 下午
**/
@Override
public Map<String, Object> getBeanToHtml(String serviceId, String catalogId) {
//String jsonStr = "";
Map<String, Object> resultMap = new HashMap<>(50);
List<SwaggerHtmlDto> result = new ArrayList<>();
try {
if (serviceId != null && !"".equals(serviceId)) {
SwaggerHtmlDto thisDto = new SwaggerHtmlDto();
//根据serviceId查询所需数据
ServiceResource resourceTable = serviceResourceDao.findById(serviceId);
List<ServiceRequest> requestList = serviceRequestDao.findByServiceId(serviceId);
List<ResponseStatus> responseStatusList = responseStatusDao.findByServiceId(serviceId);
List<ServiceResponse> serviceResponseList = serviceResponseDao.findByServiceId(serviceId);
ServiceCatalog thisCatalogBean = serviceCatalogDao.findById(catalogId == null ? "" : catalogId); //将数据set到自定义封装的dto实体中
thisDto.setServiceResource(resourceTable);
thisDto.setRequestList(requestList);
thisDto.setResponseStatusList(responseStatusList);
thisDto.setResponseList(serviceResponseList);
if (thisCatalogBean != null) {
thisDto.setTag(thisCatalogBean.getCatalogName());
String thisCatalogPid = thisCatalogBean.getPid();
if (!MAX_CATALOG_PID.equals(thisCatalogPid)) {
ServiceCatalog pidBean = serviceCatalogDao.findById(thisCatalogPid);
if (pidBean != null) {
thisDto.setTitle(pidBean.getCatalogName());
}
} else {
thisDto.setTitle(thisCatalogBean.getCatalogName());
}
}
thisDto.setVersion(resourceTable.getServiceVersion());
//将所有数据add至result
result.add(thisDto);
Map<String, List<SwaggerHtmlDto>> tableMap = new HashMap<>(50);
if (catalogId != null && thisCatalogBean != null) {
tableMap = result.stream().parallel().collect(Collectors.groupingBy(SwaggerHtmlDto::getTitle));
} else {
tableMap = result.stream().parallel().collect(Collectors.groupingBy(SwaggerHtmlDto::getVersion));
}
resultMap.put("tableMap", new TreeMap<>(tableMap));
}
} catch (Exception e) {
log.error("Javabean Convert Swagger Json Error", e);
}
return resultMap;
} }

Swagger解析工具类:

/**
* @Author Torres Liu
* @Description //TODO Swagger格式解析Json工具类
* @Date 2020/4/8 4:32 下午
* @Param
* @return
**/
public class SwaggerJsonUtils { 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();
} }

获取各种IP地址工具类:

package xxxxxxxx
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils; import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.*;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List; public class WebToolUtils {
/**
* UNKNOWN
*/
private final static String UNKNOWN = "unknown"; /**
* 获取本地IP地址
*
* @throws SocketException
*/
public static String getLocalIp() throws UnknownHostException, SocketException {
if (isWindowsOs()) {
return InetAddress.getLocalHost().getHostAddress();
} else {
return getLinuxLocalIp();
}
} /**
* 判断操作系统是否是Windows
*
* @return
*/
public static boolean isWindowsOs() {
String windowsSys = "windows";
boolean isWindowsOs = false;
String osName = System.getProperty("os.name");
if (osName.toLowerCase().indexOf(windowsSys) > -1) {
isWindowsOs = true;
}
return isWindowsOs;
} /**
* 获取本地Host名称
*/
public static String getLocalHostName() throws UnknownHostException {
return InetAddress.getLocalHost().getHostName();
} /**
* 获取Linux下的IP地址
*
* @return IP地址
* @throws SocketException
*/
private static String getLinuxLocalIp() throws SocketException {
String ip = "";
try {
for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements(); ) {
NetworkInterface intf = en.nextElement();
String name = intf.getName();
if (!name.contains("docker") && !name.contains("lo")) {
for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();) {
InetAddress inetAddress = enumIpAddr.nextElement();
if (!inetAddress.isLoopbackAddress()) {
String ipaddress = inetAddress.getHostAddress().toString();
if (!ipaddress.contains("::") &&
!ipaddress.contains("0:0:") &&
!ipaddress.contains("fe80")) {
ip = ipaddress;
System.out.println(ipaddress);
}
}
}
}
}
} catch (SocketException ex) {
System.out.println("获取ip地址异常");
ip = "127.0.0.1";
ex.printStackTrace();
}
System.out.println("IP:" + ip);
return ip;
} /**
* 获取用户真实IP地址,不使用request.getRemoteAddr();的原因是有可能用户使用了代理软件方式避免真实IP地址,
* <p>
* 可是,如果通过了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP值,究竟哪个才是真正的用户端的真实IP呢?
* 答案是取X-Forwarded-For中第一个非unknown的有效IP字符串。
* <p>
* 如:X-Forwarded-For:192.168.1.110, 192.168.1.120, 192.168.1.130,
* 192.168.1.100
* <p>
* 用户真实IP为: 192.168.1.110
*
* @param request
* @return
*/
public static String getIpAddress(HttpServletRequest request) {
String ip = request.getHeader("x-forwarded-for");
if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_CLIENT_IP");
}
if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return ip;
} /**
* 向指定URL发送GET方法的请求
*
* @param url 发送请求的URL
* @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
* @return URL 所代表远程资源的响应结果
*/
public static String sendGet(String url, String param) {
StringBuffer result = new StringBuffer();
BufferedReader in = null;
try {
String urlNameString = url + "?" + param;
URL realUrl = new URL(urlNameString);
// 打开和URL之间的连接
URLConnection connection = realUrl.openConnection();
// 设置通用的请求属性
connection.setRequestProperty("accept", "*/*");
connection.setRequestProperty("connection", "Keep-Alive");
connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
// 建立实际的连接
connection.connect();
// 定义 BufferedReader输入流来读取URL的响应
in = new BufferedReader(new
InputStreamReader(connection.getInputStream(),"UTF-8"));
String line;
while ((line = in.readLine()) != null) {
result.append(line);
}
} catch (Exception e) {
System.out.println("发送GET请求出现异常!" + e);
e.printStackTrace();
}
// 使用finally块来关闭输入流
finally {
try {
if (in != null) {
in.close();
}
} catch (Exception e2) {
e2.printStackTrace();
}
}
return result.toString();
} /**
* 向指定 URL 发送POST方法的请求
* @param pathUrl
* @param name
* @param pwd
* @param phone
* @param content
*/
public static void sendPost(String pathUrl, String name, String pwd, String phone, String content) {
try {
// 建立连接
URL url = new URL(pathUrl);
HttpURLConnection httpConn = (HttpURLConnection) url.openConnection(); // //设置连接属性
// 使用 URL 连接进行输出
httpConn.setDoOutput(true);
// 使用 URL 连接进行输入
httpConn.setDoInput(true);
// 忽略缓存
httpConn.setUseCaches(false);
// 设置URL请求方法
httpConn.setRequestMethod("POST");
String requestString = "客服端要以以流方式发送到服务端的数据..."; // 设置请求属性
// 获得数据字节数据,请求数据流的编码,必须和下面服务器端处理请求流的编码一致
byte[] requestStringBytes = requestString.getBytes("utf-8");
httpConn.setRequestProperty("Content-length", "" + requestStringBytes.length);
httpConn.setRequestProperty("Content-Type", " application/x-www-form-urlencoded");
// 维持长连接
httpConn.setRequestProperty("Connection", "Keep-Alive");
httpConn.setRequestProperty("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
httpConn.setRequestProperty("Accept-Encoding", "gzip, deflate");
httpConn.setRequestProperty("Accept-Language", "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3");
httpConn.setRequestProperty("User-Agent",
"Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:49.0) Gecko/20100101 Firefox/49.0");
httpConn.setRequestProperty("Upgrade-Insecure-Requests", "1"); httpConn.setRequestProperty("account", name);
httpConn.setRequestProperty("passwd", pwd);
httpConn.setRequestProperty("phone", phone);
httpConn.setRequestProperty("content", content); // 建立输出流,并写入数据
OutputStream outputStream = httpConn.getOutputStream();
outputStream.write(requestStringBytes);
outputStream.close();
// 获得响应状态
int responseCode = httpConn.getResponseCode();
// 连接成功
if (HttpURLConnection.HTTP_OK == responseCode) {
// 当正确响应时处理数据
StringBuffer sb = new StringBuffer();
String readLine;
BufferedReader responseReader;
// 处理响应流,必须与服务器响应流输出的编码一致
responseReader = new BufferedReader(new InputStreamReader(httpConn.getInputStream(), "utf-8"));
while ((readLine = responseReader.readLine()) != null) {
sb.append(readLine).append("\n");
}
responseReader.close();
}
} catch (Exception ex) {
ex.printStackTrace();
}
} /**
* 执行一个HTTP POST请求,返回请求响应的HTML
* @param url
* @param name
* @param pwd
* @param phone
* @param content
*/
public static void doPost(String url, String name, String pwd, String phone, String content) {
// 创建默认的httpClient实例.
CloseableHttpClient httpclient = HttpClients.createDefault();
// 创建httppost
HttpPost httppost = new HttpPost(url);
// 创建参数队列
List<NameValuePair> formparams = new ArrayList<NameValuePair>();
formparams.add(new BasicNameValuePair("account", name));
formparams.add(new BasicNameValuePair("passwd", pwd));
formparams.add(new BasicNameValuePair("phone", phone));
formparams.add(new BasicNameValuePair("content", content)); UrlEncodedFormEntity uefEntity;
try {
uefEntity = new UrlEncodedFormEntity(formparams, "UTF-8");
httppost.setEntity(uefEntity);
System.out.println("executing request " + httppost.getURI());
CloseableHttpResponse response = httpclient.execute(httppost);
try {
HttpEntity entity = response.getEntity();
if (entity != null) {
System.out.println("--------------------------------------");
System.out.println("Response content: " + EntityUtils.toString(entity, "UTF-8"));
System.out.println("--------------------------------------");
}
} finally {
response.close();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 关闭连接,释放资源
try {
httpclient.close();
} catch (IOException e) {
e.printStackTrace();
}
} }
}

以上便是后端解析的代码(dao接口和mapper.xml的sql我这边忽略了,可以根据自己实际业务去写),最后附上前端代码:

前端api详情页代码:

前端这边使用了thymeleaf模板引擎。

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="Content-Type" content="application/msword"/>
<div th:each="tableMap:${tableMap}">
<title th:each="table:${tableMap.value}" th:if="${table.title} != null" th:text="${table.title + '(1.0)详情页'}"></title>
<title th:each="table:${tableMap.value}" th:if="${table.title} == null" th:text="${table.serviceResource.serviceName}"></title>
</div> <style type="text/css">
.swaggerApi {
border-radius: 15px;
}
.swaggerApi .bg {
color: #000000;
/*background-color: #69b869;*/
} .swaggerApi .trBgA {
color: #000000;
background-color: #d9edf7;
}
.swaggerApi .trBgA:hover {
background-color: #d9edf7;
} .swaggerApi .trBgB {
color: #000000;
background-color: #fcf8e3;
}
.swaggerApi .trBgB:hover {
background-color: #fcf8e3;
} .swaggerApi .titleTagA {
color: #337ab7;
background-color: #d9edf7;
font-size: 18px;
font-weight: 600;
} .swaggerApi .titleTagB {
color: #aa7408;
background-color: #fcf8e3;
font-size: 18px;
font-weight: 600;
} .swaggerApi .titleTagC {
color: #5cb85c;
background-color: #dff0d8;
font-size: 18px;
font-weight: 600;
} .swaggerApi table {
padding: 10px;
border: 1px solid #dbe3e4;
table-layout: fixed;
color: #333333;
background-color: #ffffff;
} .swaggerApi tr {
height: 36px;
font-size: 16px;
} .swaggerApi tr:hover{
background-color: #f5f5f5;
} .swaggerApi td {
padding: 8px;
border: 1px solid #ddd;
height: 36px;
overflow: hidden;
word-break: break-all;
word-wrap: break-word;
font-size: 16px;
font-family: 宋体;
} .swaggerApi .first_title {
/*color: #eee;*/
height: 60px;
line-height: 60px;
margin: 0;
font-weight: bold;
font-size: 20px;
font-family: 宋体;
} .swaggerApi .second_title {
/*color: #eee;*/
height: 40px;
line-height: 40px;
margin: 0;
font-size: 16px;
font-family: 宋体;
} .swaggerApi .doc_title {
color: #eee;
font-size: 24px;
text-align: center;
font-weight: bold;
border-bottom: 1px solid #eee;
padding-bottom: 9px;
margin: 34px 0 20px;
font-family: 宋体;
} .swaggerApi .download_btn_def {
float: right;
margin-left: 10px;
display: inline-block;
height: 38px;
line-height: 38px;
padding: 0 18px;
background-color: #009688;
color: #fff;
white-space: nowrap;
text-align: center;
font-size: 14px;
border: none;
border-radius: 2px;
cursor: pointer;
} .swaggerApi .download_btn_def:hover {
opacity: 0.8;
} .swaggerApi .download_btn_blue {
float: right;
margin-left: 10px;
display: inline-block;
height: 38px;
line-height: 38px;
padding: 0 18px;
background-color: #1E9FFF;
color: #fff;
white-space: nowrap;
text-align: center;
font-size: 14px;
border: none;
border-radius: 2px;
cursor: pointer;
}
.swaggerApi .download_btn_blue:hover {
opacity: 0.8;
} .swaggerApi .alert {
padding: 15px;
margin-bottom: 5px;
border: 1px solid transparent;
border-radius: 4px;
} .swaggerApi .alert-warning {
color: #8a6d3b;
background-color: #fcf8e3;
border-color: #faebcc;
} </style>
</head> <body>
<div class="swaggerApi" style="margin: 0 auto;padding:0 40px">
<div th:each="tableMap:${tableMap}">
<div th:each="table:${tableMap.value}">
<!-- <p class="doc_title" th:text="${tableMap.key+'('+ table.version +')'}"></p>-->
<div class=" doc_title">
<div th:if="${table.title} != null" th:text="${table.title+'('+ table.version +')'}"></div>
<div th:if="${table.title} == null" th:text="${table.serviceResource.serviceName} + '-详情页'"></div>
</div>
<!-- <a type="button" class="download_btn btn btn-danger glyphicon glyphicon-cloud-download" disabled="disabled" th:if="${download == 1}" href="#">下载(pdf)</a>-->
<a type="button" class="download_btn_def" th:if="${download == 1}" th:href="${ipAndPort + '/beanToSwaggerHtml/downloadWordByBean?serviceId='+ serviceId +'&catalogId='+catalogId}+'&outputType=excel'">下载(excel)</a>
<a type="button" class="download_btn_blue" th:if="${download == 1}" th:href="${ipAndPort + '/beanToSwaggerHtml/downloadWordByBean?serviceId='+ serviceId +'&catalogId='+catalogId}+'&outputType=word' ">下载(doc)</a>
<br>
</div>
</div>
<div th:each="tableMap:${tableMap}" style="margin-bottom:20px;margin-top: 40px">
<div th:each="table,tableStat:${tableMap.value}">
<div class="alert alert-warning">
<strong>提示:</strong><p>调用时需在请求头添加凭证,格式如下</p>
<strong>Request Headers:</strong>
<p>Authorization : Basic Base64(ak:sk)</p>
</div>
<h4 class="first_title" th:if="${table.tag} != null" th:text="${table.tag}"></h4>
<!--这个是每个请求的说明,方便生成文档后进行整理-->
<br th:if="${tableStat.index != 0}">
<h5 class="second_title" th:text="${tableStat.count} + ')' + ${table.serviceResource.serviceName}"></h5> <table class="" border="1" cellspacing="0" cellpadding="0" width="100%">
<tr align="left">
<td class="titleTagC" colspan="5">ServiceInfo</td>
</tr>
<tbody class="">
<tr>
<td width="25%">服务名称</td>
<td colspan="4" th:text="${table.serviceResource.serviceName}"></td>
</tr>
<tr>
<td width="25%">服务描述</td>
<td colspan="4" th:if="${table.serviceResource.serviceDesc} != '' " th:text="${table.serviceResource.serviceDesc} != 'null' ? ${table.serviceResource.serviceDesc} : '无'"></td>
<td colspan="4" th:if="${table.serviceResource.serviceDesc} == '' " th:text="无"></td>
</tr>
<tr>
<td>请求地址</td>
<td colspan="4" th:text="${table.serviceResource.requestUrl}"></td>
</tr>
<tr>
<td>请求方法</td>
<td colspan="4" th:text="${table.serviceResource.requestMethod}"></td>
</tr>
<tr>
<td>请求类型</td>
<td colspan="4" th:if="${table.serviceResource.contentType} == '' " th:text="无"></td>
<td colspan="4" th:if="${table.serviceResource.contentType} != '' " th:text="${table.serviceResource.contentType}"></td>
</tr>
<tr>
<td>返回类型</td>
<td colspan="4" th:if="${table.serviceResource.callContentType} == '' " th:text="无"></td>
<td colspan="4" th:if="${table.serviceResource.callContentType} != '' " th:text="${table.serviceResource.callContentType}"></td>
</tr>
</tbody> <tr align="left">
<td class="titleTagA" colspan="5">Parameters</td>
</tr>
<tr class="trBgA" align="center">
<td>参数名称</td>
<td>参数类型</td>
<td>参数来源</td>
<td>是否必填</td>
<td>说明</td>
</tr>
<tbody>
<tr align="center" th:each="request:${table.requestList}">
<td th:text="${request.reqName}"></td>
<td th:text="${request.reqType}"></td>
<td th:text="${request.reqFrom}"></td>
<td th:if="${request.isRequired} == 1" th:text="是"></td>
<td th:if="${request.isRequired} == 0" th:text="否"></td>
<td th:text="${request.reqDesc}"></td>
</tr>
</tbody>
<tr align="left">
<td class="titleTagB" colspan="5">Responses</td>
</tr>
<tr class="trBgB" align="center">
<td>响应状态码</td>
<td colspan="2">描述</td>
<td colspan="2">返回说明</td>
</tr>
<tbody>
<tr align="center" valign="middle !important" th:each="responseStatus:${table.responseStatusList}">
<td th:text="${responseStatus.statusCode}"></td>
<td colspan="2" th:text="${responseStatus.statusDesc}"></td>
<td colspan="2" th:if="${responseStatus.statusRemark} == null " th:text="无"></td>
<td colspan="2" th:if="${responseStatus.statusRemark} != null " th:text="${responseStatus.statusRemark}"></td>
</tr>
<!-- </tbody>-->
<!-- <tr class="bg tagHoverColor" align="center">-->
<!-- <td>返回属性名</td>-->
<!-- <td colspan="2">类型</td>-->
<!-- <td colspan="2">说明</td>-->
<!-- </tr>-->
<!-- <tbody>-->
<!-- <tr align="center" th:each="response:${table.responseList}">-->
<!-- <td th:text="${response.propName}"></td>-->
<!-- <td colspan="2" th:text="${response.propType}"></td>-->
<!-- <td colspan="2" th:text="${response.propDesc}"></td>-->
<!-- </tr>-->
<!-- </tbody>-->
<!-- <tr>-->
<!-- <td class="titleTagD" colspan="5">Example Value</td>-->
<!-- </tr>-->
<!-- <tr class="specialHeight">-->
<!-- <td align="center">请求示例</td>-->
<!-- <td colspan="4" ></td>-->
<!-- </tr>-->
<!-- <tr class="specialHeight">-->
<!-- <td align="center">返回示例</td>-->
<!-- <td colspan="4"></td>-->
<!-- </tr>--> </table>
</div>
</div>
</div>
<script>
/*<![CDATA[*/
/**
* json美化
* jsonFormat2(json)这样为格式化代码。
* jsonFormat2(json,true)为开启压缩模式
* @param txt
* @param compress
* @returns {string}
*/
function jsonFormat(txt,compress){
debugger;
txt = JSON.stringify(txt);
//alert(txt);
var indentChar = ' ';
if(/^\s*$/.test(txt)){
alert('数据为空,无法格式化! ');
return;
}
try{var data=eval('('+txt+')');}
catch(e){
alert('数据源语法错误,格式化失败! 错误信息: '+e.description,'err');
return;
};
var draw=[],last=false,This=this,line=compress?'':'\n',nodeCount=0,maxDepth=0; var notify=function(name,value,isLast,indent/*缩进*/,formObj){
nodeCount++;/*节点计数*/
for (var i=0,tab='';i<indent;i++ )tab+=indentChar;/* 缩进HTML */
tab=compress?'':tab;/*压缩模式忽略缩进*/
maxDepth=++indent;/*缩进递增并记录*/
if(value&&value.constructor==Array){/*处理数组*/
draw.push(tab+(formObj?('"'+name+'":'):'')+'['+line);/*缩进'[' 然后换行*/
for (var i=0;i<value.length;i++)
notify(i,value[i],i==value.length-1,indent,false);
draw.push(tab+']'+(isLast?line:(','+line)));/*缩进']'换行,若非尾元素则添加逗号*/
}else if(value&&typeof value=='object'){/*处理对象*/
draw.push(tab+(formObj?('"'+name+'":'):'')+'{'+line);/*缩进'{' 然后换行*/
var len=0,i=0;
for(var key in value)len++;
for(var key in value)notify(key,value[key],++i==len,indent,true);
draw.push(tab+'}'+(isLast?line:(','+line)));/*缩进'}'换行,若非尾元素则添加逗号*/
}else{
if(typeof value=='string')value='"'+value+'"';
draw.push(tab+(formObj?('"'+name+'":'):'')+value+(isLast?'':',')+line);
};
};
var isLast=true,indent=0;
notify('',data,isLast,indent,false);
var aaaa = darw;
return draw.join('');
}
/*]]>*/
</script>
</body>
</html>

使用思路:

首先通过swagger json解析为实体类并存入数据库中(对应上面的swagger解析代码),在通过调用javabean转html的接口来实现将存入的数据转为html页面(对应上面的javabean转为html渲染页面代码)。

结尾:

总结:

其实也可以直接通过swagger json解析然后存入实体类直接渲染给页面。就是不入库直接将swaggerjson生成出html,这种方案我也实现了,但是在这篇文章中不做过多介绍,如果有需要以后我也会写篇文章做一下记录。

其实都是一样的思路啦,写代码讲究的是思路。

参考资料:

https://www.cnblogs.com/jmcui/p/8298823.html

https://github.com/JMCuixy/swagger2word

https://www.aliyun.com/product/csb?spm=5176.10695662.784136.1.57b794ceX78G27

最新文章

  1. shutil复制粘贴和压缩
  2. FTP地址格式如下:“ftp://用户名:密码@FTP服务器IP”
  3. 【Java学习笔记】foreach语句(高级for)
  4. CentOS查看软件源提供的软件版本命令
  5. Android中通过导入静态数据库来提高应用第一次的启动速度
  6. hive部署手册
  7. java web 学习三(Tomcat 服务器学习和使用2)
  8. uva 10934 Dropping water balloons(转载)
  9. poj 3487 稳定婚姻
  10. ZSTU OJ 3999 零基础学算法---邻接表
  11. avalon与双缓冲技术
  12. (BFS) leetcode 279. Perfect Squares
  13. git stash pop 冲突,git stash list 中的记录不会自动删除的解决方法
  14. [Oracle11g] 通过伪列查询
  15. 编程实践:使用java访问mySQL数据库
  16. 《DSP using MATLAB》Problem 7.2
  17. MapGIS SDK(C++)【基础篇】
  18. Centos7下不删除python2.x的情况下安装python3.x
  19. 好使-利用python 下paramiko模块无密码登录
  20. CF gym101933 K King&#39;s Colors——二项式反演

热门文章

  1. Semaphores
  2. Simple Math Problems
  3. C语言编程入门题目--No.13
  4. CF思维联系–CodeForces - 223 C Partial Sums(组合数学的先线性递推)
  5. 数学--数论--HDU1792A New Change Problem(GCD规律推导)
  6. The 2019 Asia Nanchang First Round Online Programming Contest B Fire-Fighting Hero(阅读理解)
  7. 疯子的算法总结10--最小生成树Kruscal
  8. unittest(简介)
  9. libevhtp初探
  10. 无线脉冲水表LoRaWAN方案芯片ASR6500S