hasor-spring:4.2.5
hasor-dataway:4.2.5
springboot 2.5.0
swagger:springfox-boot-starter 3.0.0
Dataway 配置swagger 后接口文档显示的永远是第一次Dataway 接口发布的内容,修改重新发布后,无法更新成最新的记录。
Dataway 和swagger 交互是通过Dataway 提供的公共默认接口 /api/docs/swagger2.json 而这个接口在Dataway接口更新重新发布后并未更新。导致swagger-ui 无法更新文档。
Swagger2Controller 是Dataway 提供的 /api/docs/swagger2.json接口
Dataway 提供两个表
interface_release(发布后,添加一条记录)
interface_info(实时记录信息,保存更新)
错误定位1:查询的是历史记录表《EntityDef.RELEASE》《EntityDef.INFO》(由于我们的需求是只显示发布的接口文档,所以此方案取消。注:该版本改了也不起作用,将导致接口数据全无,原因是sql,少查了一个关键字段 api_schema)
错误定位2:一个接口如果存在多条重复的记录,他将选择最老的一条记录
在编写一个接口,查询表中数据,返回指定格式,方式如下
由此 为了偷懒,选用方法1,作为解决方法
--------首先仿写Swagger2Controller
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletRequest;
import net.hasor.dataql.fx.basic.StringUdfSource;
import net.hasor.dataway.config.DatawayUtils;
import net.hasor.dataway.config.MappingToUrl;
import net.hasor.dataway.dal.EntityDef;
import net.hasor.dataway.dal.FieldDef;
import net.hasor.dataway.web.BasicController;
import net.hasor.dataway.web.Swagger2Query;
import net.hasor.utils.StringUtils;
import net.hasor.web.Invoker;
import net.hasor.web.annotation.Get;
import net.hasor.web.objects.JsonRenderEngine;
import net.hasor.web.render.RenderType;
@MappingToUrl("/api/docs/swagger3.json")
@RenderType(
value = "json",
engineType = JsonRenderEngine.class
)
public class Swagger3Controller extends BasicController {
public Swagger3Controller() {
}
@Get
public Object doSwaggerApi(Invoker invoker) throws IOException {
HttpServletRequest httpRequest = invoker.getHttpRequest();
String localName = httpRequest.getHeader("Host");
if (StringUtils.isBlank(localName)) {
int localPort = httpRequest.getLocalPort();
localName = httpRequest.getLocalAddr() + (localPort == 80 ? "" : ":" + localPort);
}
List<Map<FieldDef, String>> doList = this.dataAccessLayer.listObjectBy(EntityDef.RELEASE, emptyCondition());
List<Map<String,Object>> list=new ArrayList<>();
// 这里将获取的数据按接口id归类
Map<String, List<Map<FieldDef, String>>> map = doList.stream().collect(Collectors.groupingBy(item -> item.get(FieldDef.API_ID)));
List<Map<FieldDef, String>> doListNew = new ArrayList<>();
// 选择每个组中的第一个,也就是最新的记录添加到集合中,然后使用新定义的集合向下传递
map.forEach((k,v)->{
doListNew.add(v.get(0));
});
final List<Map<String, String>> collectList = (List)doListNew.stream().map((defMap) -> {
Map<String, String> dataMap = new HashMap();
defMap.forEach((fieldDef, s) -> {
dataMap.put(StringUdfSource.lineToHump(fieldDef.name()), s);
});
return dataMap;
}).collect(Collectors.toList());
String contextPathProxy = invoker.getHttpRequest().getParameter("DW_CONTEXT_PATH_PROXY");
final String contextPath = DatawayUtils.getDwContextPath(invoker, contextPathProxy);
final String locName = localName;
return (new Swagger2Query()).execute(new HashMap<String, Object>() {
{
this.put("apiDataList", collectList);
this.put("serverHost", locName);
this.put("serverBasePath", StringUtils.isNotBlank(contextPath) ? contextPath : "/");
}
}).getData().unwrap();
}
}
—查看源码得知DatawayModule中加载的Swagger2Controller接口 ; 仿写加载过程,
由于我们在集成Dataway时也会定义一个Module的配置类,所以我们可以直接在那里加载我们编写的Swagger3Controller
import com.bksx.easy_config.dataway.controller.Swagger3Controller;
import com.bksx.easy_config.dataway.spi.FxSqlCheckChain;
import net.hasor.core.ApiBinder;
import net.hasor.core.DimModule;
import net.hasor.dataql.DimUdf;
import net.hasor.dataql.DimUdfSource;
import net.hasor.dataql.QueryApiBinder;
import net.hasor.dataql.fx.db.FxSqlCheckChainSpi;
import net.hasor.dataway.config.MappingToUrl;
import net.hasor.db.JdbcModule;
import net.hasor.db.Level;
import net.hasor.spring.SpringModule;
import net.hasor.utils.StringUtils;
import net.hasor.web.WebApiBinder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import javax.sql.DataSource;
@DimModule
@Component
public class HasorDataModule implements SpringModule {
@Autowired
private DataSource dataSource = null;
@Autowired
private Environment environment;
@Override
public void loadModule(ApiBinder apiBinder) throws Throwable {
// .DataSource form Spring boot into Hasor
apiBinder.installModule(new JdbcModule(Level.Full, this.dataSource));
//apiBinder.loadModule(LoadSwagger3Module.class);
//((WebApiBinder) apiBinder).loadMappingTo(Swagger3Controller.class);
// 加载自定义查询方法
// .custom DataQL
apiBinder.tryCast(QueryApiBinder.class).loadUdfSource(apiBinder.findClass(DimUdf.class)); // 注册工具类、查询类
apiBinder.tryCast(QueryApiBinder.class).loadUdfSource(apiBinder.findClass(DimUdfSource.class)); // 注册工具类、查询类
//apiBinder.tryCast(QueryApiBinder.class).bindFragment("sql", SqlFragment.class);//注册自定义语法
//apiBinder.bindSpiListener(AuthorizationChainSpi.class,AuthorizationChain.class);// 注册后台用户验证拦截器
//apiBinder.bindSpiListener(CompilerSpiListener.class,CompilerListener.class);// 注册编译拦截器,可以改写脚本
apiBinder.bindSpiListener(FxSqlCheckChainSpi.class, FxSqlCheckChain.class);// 注册sql验证拦截器
//apiBinder.bindSpiListener(LoginPerformChainSpi.class,LoginPerformChain.class);// 注册后台用户验证拦截器
//apiBinder.bindSpiListener(LoginTokenChainSpi.class,LoginTokenChain.class);// 注册后台用户验证拦截器
//apiBinder.bindSpiListener(LookupConnectionListener.class,LookupConnectionListeners.class);// 注册数据源拦截器,自定义动态数据源
//apiBinder.bindSpiListener(LookupDataSourceListener.class,LookupDataSourceListeners.class);// 注册数据源拦截器
//apiBinder.bindSpiListener(PreExecuteChain.class,PreExecuteChainSpi.class);// 注册请求拦截器
//apiBinder.bindSpiListener(ResultProcessChain.class,ResultProcessChainSpi.class);// 注册返回结果拦截器(成功、失败)
//apiBinder.bindSpiListener(SerializationChain.class,SerializationChainSpi.class);// 注册序列化截器
// 添加Dataway 接口公共方法
addControllers(apiBinder,Swagger3Controller.class);
}
/**
* 添加
* @param apiBinder
* @param aClasss
*/
private void addControllers(ApiBinder apiBinder,Class... aClasss){
String enable = environment.getProperty("HASOR_DATAQL_DATAWAY");
if(StringUtils.isBlank(enable)||"false".equals(enable)){
return;
}
String adminUrl=environment.getProperty("HASOR_DATAQL_DATAWAY_UI_URL");
Assert.notNull(adminUrl,"Dataway已开启,必须指定管理访问路径:HASOR_DATAQL_DATAWAY_UI_URL");
String apiUrl = environment.getProperty("HASOR_DATAQL_DATAWAY_API_URL");
Assert.notNull(apiUrl,"Dataway已开启,必须指定API访问路径:HASOR_DATAQL_DATAWAY_API_URL");
WebApiBinder webApiBinder = (WebApiBinder)apiBinder.tryCast(WebApiBinder.class);
if (webApiBinder != null&&aClasss!=null&&aClasss.length>0) {
for (Class aClass : aClasss) {
ApiBinder.MetaDataBindingBuilder<?> metaDataBinder = apiBinder.bindType(aClass).asEagerSingleton();
metaDataBinder.metaData("KEY_DATAWAY_UI_BASE_URI", adminUrl);
metaDataBinder.metaData("KEY_DATAWAY_API_BASE_URI", apiUrl);
MappingToUrl toUrl = (MappingToUrl)aClass.getAnnotation(MappingToUrl.class);
String url = (adminUrl + toUrl.value()).replaceAll("/+", "/");
webApiBinder.mappingTo(url, new String[0]).with(metaDataBinder.toInfo());
}
}
}
}
修改swagger 配置的Dataway 接口路径
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import springfox.documentation.swagger.web.SwaggerResource;
import springfox.documentation.swagger.web.SwaggerResourcesProvider;
import java.util.ArrayList;
import java.util.List;
@Component
@Primary
public class SwaggerProvider implements SwaggerResourcesProvider {
@Override
public List<SwaggerResource> get() {
List<SwaggerResource> resources = new ArrayList<>();
resources.add(swaggerResource("应用接口", "/v3/api-docs", "1.0"));
// 修改新的路径
resources.add(swaggerResource("Dataway接口", "/interface-ui/api/docs/swagger3.json", "1.0"));
return resources;
}
private SwaggerResource swaggerResource(String name, String location, String version) {
SwaggerResource swaggerResource = new SwaggerResource();
swaggerResource.setName(name);
swaggerResource.setLocation(location);
swaggerResource.setSwaggerVersion(version);
return swaggerResource;
}
}
封装成方法的原因是为了可以同时加载多个自定义DataWay接口
访问路径:http://ip:port/data/interface-ui/api/docs/swagger3.json