smart-doc+knife4j统一接口管理平台实战

smart-doc相比于swagger无代码侵入,只需要标准的java注释即可生成接口文档。

knife4j是接口文档的ui,更美观实用。

原理:通过smart-doc生成标准的openapi接口文档,使用knife4j-ui展示。

单体服务集成smart-doc

工程的pom.xml中添加smart-doc-maven-plugin插件和knife4j相关依赖

    <dependencies>
        <dependency>
            <groupId>org.springdoc</groupId>
            <artifactId>springdoc-openapi-ui</artifactId>
            <version>1.6.14</version>
        </dependency>
        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>knife4j-openapi3-ui</artifactId>
            <version>4.0.0</version>
        </dependency>
    </dependencies>


<build>
        <finalName>app</finalName>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>com.github.shalousun</groupId>
                <artifactId>smart-doc-maven-plugin</artifactId>
                <version>2.5.3</version>
                <configuration>
                    <!--指定生成文档的使用的配置文件,配置文件放在自己的项目中-->
                    <configFile>./src/main/resources/smart-doc.json</configFile>
                    <!--指定项目名称-->
                    <projectName>${artifactId}</projectName>
                    <!--smart-doc实现自动分析依赖树加载第三方依赖的源码,如果一些框架依赖库加载不到导致报错,这时请使用excludes排除掉-->
                    <excludes>
                        <!--格式为:groupId:artifactId;参考如下-->
                        <!--也可以支持正则式如:com.alibaba:.* -->
                    </excludes>
                    <!--includes配置用于配置加载外部依赖源码,配置后插件会按照配置项加载外部源代码而不是自动加载所有,因此使用时需要注意-->
                    <!--smart-doc能自动分析依赖树加载所有依赖源码,原则上会影响文档构建效率,因此你可以使用includes来让插件加载你配置的组件-->
                    <includes>
                        <!--格式为:groupId:artifactId;参考如下-->
                        <!--也可以支持正则式如:com.alibaba:.* -->
                        <include>com.alibaba:fastjson</include>
                        <!-- 如果配置了includes的情况下, 使用了jpa的分页需要include所使用的源码包 -->
                        <include>org.springframework.data:spring-data-commons</include>
                    </includes>
                </configuration>
                <executions>
                    <execution>
                        <!--如果不需要在执行编译时启动smart-doc,则将phase注释掉-->
                        <phase>generate-resources</phase>
                        <goals>
                            <!--smart-doc提供了html、openapi、markdown等goal,可按需配置-->
                            <goal>openapi</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

application.yaml配置文件中添加配置

springdoc:
  swagger-ui:
    url: /doc/openapi.json

创建smart-doc.json文件

# 更多配置可以见smart-doc官网
{
  "outPath": "src/main/resources/static/doc",
  "displayActualType": true
}

工程编译后会自动生成openapi.json接口文档

编写controller,一定要注意注释规范 ,否则接口无法生成

/**
 * 测试接口
 *
 * @author ***
 * @date 2023/2/2 16:36
 */
@RestController
public class TestController {


    /**
     * 测试方法
     *
     * @param param 请求query
     * @return 返回值
     */
    @RequestMapping(value = "/test", method = RequestMethod.POST)
    public String navigation(@RequestBody JSONObject param) {
        return "";
    }

}

启动服务

单机的接口文档就实现了,但是如果有100个微服务,怎么把这些所有的微服务集成到一起。常见的方案一般在网关统一汇总,这里采用单独部署一个接口文档服务的方式,通过nacos读取服务列表。

统一接口文档服务

创建一个 springboot服务,添加nacos依赖和接口文档依赖

        <!--nacos start-->
        <!--注册中心-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!--配置中心-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>
        <!--开启bootstrap-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-bootstrap</artifactId>
        </dependency>
        <!--nacos end-->

        <!--接口文档-->
        <dependency>
            <groupId>org.springdoc</groupId>
            <artifactId>springdoc-openapi-ui</artifactId>
            <version>1.6.14</version>
        </dependency>
        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>knife4j-openapi3-ui</artifactId>
            <version>4.0.0</version>
        </dependency>

创建ApiDocController,拦截所有doc/openapi.json请求,前缀即为nacos中服务名称,转发到对应的服务中以获取服务对应的openapi.json

@RestController
public class ApiDocController {

    private static final String HEADER_STR = "{\"schema\": {\"format\": \"string\", \"type\": \"string\"}, \"in\": \"header\", \"description\": \"\", \"required\": false, \"example\": \"\", \"name\": \"Authorization\"}";


    @GetMapping("/*/doc/openapi.json")
    public JSONObject router(HttpServletRequest request) {

        if (StringUtils.equals(request.getRequestURI(), "/api-doc/doc/openapi.json")) {
            return new JSONObject();
        }
        String servicePath = request.getRequestURI().split("/")[1];

        return modifyBody(WebClientUtil.restGet(String.format(ApiDocConfig.URL, servicePath)).toJSONString());
    }


    private JSONObject modifyBody(String jsonStr) {
        try {
            JSONObject json = JSONObject.parseObject(jsonStr);
            JSONObject paths = json.getJSONObject("paths");
            String basePath = json.getJSONObject("info").getString("title");
            Set<String> removePath = new HashSet<>(paths.keySet());
            for (String path : removePath) {
                paths.getJSONObject(path).forEach((k, v) -> {
                    JSONObject params = (JSONObject) v;
                    JSONArray parameters = params.getJSONArray("parameters");
                    parameters.add(getHeader());
                });
                paths.put("/" + basePath + path, paths.get(path));
                paths.remove(path);
            }
            return json;
        } catch (Exception e) {
            return null;
        }
    }


    private JSONObject getHeader() {
        return JSON.parseObject(HEADER_STR);
    }


}

定时任务从nacos中获取服务列表,初始化接口文档数据到SwaggerUiConfigProperties中

@Component
public class ApiDocConfig {

    @Resource
    private NacosServiceDiscovery nacosServiceDiscovery;

    @Resource
    private SwaggerUiConfigProperties swaggerUiConfigProperties;

    private final RestTemplate restTemplate;

    {
        HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
        factory.setConnectionRequestTimeout(1000);
        factory.setConnectTimeout(1000);
        factory.setReadTimeout(1000);
        restTemplate = new RestTemplate(factory);
    }


    public static final String URL = "http://%s:8080/doc/openapi.json";

    @PostConstruct
    @Scheduled(fixedRate = 1000 * 60 * 10)
    public void init() throws NacosException {
        List<String> services = nacosServiceDiscovery.getServices();
        for (String service : services) {
            JSONObject jsonObject = new JSONObject();
            try {
                //校验文件是否存在
                ResponseEntity<JSONObject> response = restTemplate.getForEntity(String.format(URL, service), JSONObject.class);
                jsonObject = response.getBody();
            } catch (Exception ignored) {
                continue;
            }
            if (Objects.isNull(jsonObject) || jsonObject.containsKey("msg")) {
                continue;
            }
            AbstractSwaggerUiConfigProperties.SwaggerUrl swaggerUrl = new AbstractSwaggerUiConfigProperties.SwaggerUrl(service, service + "/doc/openapi.json", service);
            Set<AbstractSwaggerUiConfigProperties.SwaggerUrl> urls = swaggerUiConfigProperties.getUrls();
            if (urls == null) {
                urls = new LinkedHashSet<>();
                swaggerUiConfigProperties.setUrls(urls);
            }
            urls.add(swaggerUrl);
        }
    }


}

因为openapi.json是静态文件,浏览器会缓存,加一下nocache的配置防止无法加载新的数据

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        WebContentInterceptor interceptor = new WebContentInterceptor();
        interceptor.addCacheMapping(CacheControl.noCache().cachePrivate().noTransform(), "/**");
        registry.addInterceptor(interceptor);

    }

}
@EnableScheduling
@SpringBootApplication(scanBasePackages = "com.poorbusy")
public class ApiDocApplication {

    public static void main(String[] args) {
        SpringApplication.run(ApiDocApplication.class, args);
        System.out.println("启动成功");
    }

}

启动服务,访问接口文档地址就可以看到聚合了所有服务的接口文档了,下拉可以切换服务

一个统一的接口文档管理平台就大功告成了。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值