Use version
hasor-spring: 4.2.5
hasor-dataway: 4.2.5
springboot 2.5.0
swagger:springfox-boot-starter 3.0.0
Description of the problem
After Dataway configures swagger, the interface document will always display the content published by the Dataway interface for the first time. After modification and re-release, it cannot be updated to the latest record.
Issue Tracking
The interaction between Dataway and swagger is through the public default interface /api/docs/swagger2.json provided by Dataway, and this interface has not been updated after the Dataway interface was updated and re-released. Cause swagger-ui can’t update the document.
Swagger2Controller is the /api/docs/swagger2.json interface provided by Dataway
Dataway provides two tables
interface_release (after release, add a record)
interface_info (record information in real time, save update)
Error location 1: The history table “EntityDef.RELEASE” and “EntityDef.INFO” are queried (since our requirement is to only display the published interface documents, this solution is cancelled. Note: This version does not work, and the As a result, there is no interface data, the reason is sql, and a key field api_schema was missing)
Error location 2: If an interface has multiple duplicate records, it will select the oldest record
Solution ideas
When writing an interface, query the data in the table, and return the specified format, the method is as follows
- Write Swagger3Controller like Swagger2Controller
- custom interface
- Rewrite Swqgger2Query, (but because this class does not understand, so give up this method; if you understand, please let me know)
Therefore, in order to be lazy, choose method 1 as a solution
——–First imitate 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<>(); // Here the acquired data is classified by interface 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<>(); // Select the first in each group, that is, add the newest record to the collection, and then use the newly defined collection to pass down 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(); } }
-Check the source code to know the Swagger2Controller interface loaded in DatawayModule; imitate the loading process,
Since we also define a Module configuration class when integrating Dataway, we can directly load the Swagger3Controller we wrote there
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); // load custom query method // .custom DataQL apiBinder.tryCast(QueryApiBinder.class).loadUdfSource(apiBinder.findClass(DimUdf.class)); // register tool class and query class apiBinder.tryCast(QueryApiBinder.class).loadUdfSource(apiBinder.findClass(DimUdfSource.class)); // register tool class and query class //apiBinder.tryCast(QueryApiBinder.class).bindFragment("sql", SqlFragment.class);//Register custom syntax //apiBinder.bindSpiListener(AuthorizationChainSpi.class,AuthorizationChain.class);// Register background user verification interceptor //apiBinder.bindSpiListener(CompilerSpiListener.class,CompilerListener.class);// Register compilation interceptor, you can rewrite the script apiBinder.bindSpiListener(FxSqlCheckChainSpi.class, FxSqlCheckChain.class);// register sql verification interceptor //apiBinder.bindSpiListener(LoginPerformChainSpi.class, LoginPerformChain.class);// Register background user authentication interceptor //apiBinder.bindSpiListener(LoginTokenChainSpi.class, LoginTokenChain.class);// Register background user authentication interceptor //apiBinder.bindSpiListener(LookupConnectionListener.class,LookupConnectionListeners.class);// Register data source interceptor, customize dynamic data source //apiBinder.bindSpiListener(LookupDataSourceListener.class, LookupDataSourceListeners.class);// Register data source interceptor //apiBinder.bindSpiListener(PreExecuteChain.class,PreExecuteChainSpi.class);// Register request interceptor //apiBinder.bindSpiListener(ResultProcessChain.class,ResultProcessChainSpi.class);// Register return result interceptor (success, failure) //apiBinder.bindSpiListener(SerializationChain.class,SerializationChainSpi.class);// Register serializer // Add Dataway interface public method addControllers(apiBinder, Swagger3Controller. class); } /** * Add to * @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 is enabled, you must specify the management access path: HASOR_DATAQL_DATAWAY_UI_URL"); String apiUrl = environment. getProperty("HASOR_DATAQL_DATAWAY_API_URL"); Assert.notNull(apiUrl,"Dataway is enabled, API access path must be specified: HASOR_DATAQL_DATAWAY_API_URL"); WebApiBinder webApiBinder = (WebApiBinder)apiBinder. tryCast(WebApiBinder. class); if (webApiBinder != null & amp; & amp;aClasss!=null & amp; & amp;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()); } } } }
Modify the Dataway interface path of the swagger configuration
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("Application Interface", "/v3/api-docs", "1.0")); // Modify the new path resources.add(swaggerResource("Dataway interface", "/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; } }
The reason for encapsulating it into a method is to load multiple custom DataWay interfaces at the same time
Access path: http://ip:port/data/interface-ui/api/docs/swagger3.json