关于Dataway 集成 swagger 接口更新后,在swagger web端无法更新

使用版本

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 集成 swagger 接口更新后,在swagger web端无法更新_第1张图片
Dataway 提供两个表
interface_release(发布后,添加一条记录)
interface_info(实时记录信息,保存更新)

错误定位1:查询的是历史记录表《EntityDef.RELEASE》《EntityDef.INFO》(由于我们的需求是只显示发布的接口文档,所以此方案取消。注:该版本改了也不起作用,将导致接口数据全无,原因是sql,少查了一个关键字段 api_schema
错误定位2:一个接口如果存在多条重复的记录,他将选择最老的一条记录

解决思路

在编写一个接口,查询表中数据,返回指定格式,方式如下

  1. 仿照Swagger2Controller 编写Swagger3Controller
  2. 自定义接口
  3. 重写Swqgger2Query,(但是由于这个类没看懂,所以放弃该 方法;有懂的请不吝赐教)

由此 为了偷懒,选用方法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

你可能感兴趣的:(前端,java,spring)