spring cloud作为微服务的解决方案,有gateway这种网关,自然也要有opendoc这种统一的文档测试模块,可惜并没有,但是我们可以自己基于spring gateway自己构建一个,下面介绍下风铃的opendoc的构建方案
1.需求
前后端分离,前端需要后端的文档辅助自己的开发,同时后端也不想花费过多的时间再这个上面
2.方案
百度许久感觉还是swagger方案最简单,普及率高,使用方便,符合现代风格,ui和服务分离也容易改造,也找到了一个自定义ui的方案
swagger-bootstrap-ui,这样我们的方案就出来了
基于spring gateway做请求转发的代理,基于swagger做后台的文档生产,基于swagger-bootstrap-ui做前段UI,因为风格和风铃不匹配打算自己改
3.实现
每个服务都集成swgger,用于文档生成和自己测试
<dependency>
<groupId>io.springfoxgroupId>
<artifactId>springfox-swagger2artifactId>
dependency>
<dependency>
<groupId>com.github.xiaoymingroupId>
<artifactId>swagger-bootstrap-uiartifactId>
dependency>
然后opendoc配置和gateway一样都集成spring gateway和nacos用于访问服务和读取文档
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-gatewayartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-configartifactId>
dependency>
这样我们opendoc功能和gateway一致,也可以通过后缀,做到跨域访问后缀
下一步就是查看一下,swagger-bootstrap-ui的逻辑,然后我们替换成自己的代码模拟swagger 的请求(其实就是透传,组合)
swagger主要有连个请求一个是获取所有的service,就是所有的后台请求组:
@Bean
public Docket createSystemRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.groupName("基础管理")
.select()
.apis(RequestHandlerSelectors.basePackage("cn.afterturn.boot.admin"))
.paths(PathSelectors.any())
.build()
.globalResponseMessage(RequestMethod.GET, getResponseMessages())
.globalResponseMessage(RequestMethod.POST, getResponseMessages())
.globalResponseMessage(RequestMethod.PUT, getResponseMessages())
.globalResponseMessage(RequestMethod.DELETE, getResponseMessages());
}
随便说下,因为spring cloud的FeignClient 实际上也是controller 并且注解基本上也是一样的,所以我们最好是扫码包,不要直接扫描@ApiOperation注解,会把依赖加进来也就是重复的数据
模拟service
去掉gateway和opendoc两个自己注册的服务,没有接口提供
把所有注册服务的group都统计到一起,返回给前端
@GetMapping("/services")
public List<SwaggerProjectModel> serviceUrl() {
List<ServiceInstance> serviceInstanceList = getServiceInstanceList();
List<SwaggerProjectModel> services = new ArrayList<>();
SwaggerProjectModel model;
for (int i = 0; i < serviceInstanceList.size(); i++) {
try {
if (serviceInstanceList.get(i).getServiceId().equalsIgnoreCase("lemur-gateway")
|| serviceInstanceList.get(i).getServiceId().equalsIgnoreCase("lemur-opendoc")) {
continue;
}
log.debug("start get third services success");
List<SwaggerProjectModel> tempList = JSON.parseArray(HttpUtil.get(serviceInstanceList.get(i).getUri().toString() + "/swagger-resources", 60 * 1000), SwaggerProjectModel.class);
log.debug("end get third services success");
for (int j = 0; j < tempList.size(); j++) {
model = tempList.get(j);
model.setName(serviceInstanceList.get(i).getServiceId() + ":" + model.getName());
model.setUrl("/doc/group/" + serviceInstanceList.get(i).getServiceId() + "/" + getGroup(model.getUrl()));
model.setPrefix(prefixMap.get(getRealServiceName(serviceInstanceList.get(i).getServiceId())));
model.setBasePath(prefixMap.get(getRealServiceName(serviceInstanceList.get(i).getServiceId())));
services.add(model);
}
} catch (Exception e) {
}
}
log.debug("get services success");
return services;
}
前端要的就是这个列表,其中basePath就是我们gateway转发的地址,方便gateway转发
private String location;
private String name;
private String swaggerVersion;
private String url;
private String basePath;
然后前端就获取的所有的group,下一步就是根据group获取下面所有的函数的,也很简单,把服务透传到下面去就可以了
@GetMapping("/group/{serviceId}/{group}")
public String group(@PathVariable String serviceId, @PathVariable String group) {
if (group.equalsIgnoreCase("dubbo")) {
return HttpUtil.get(discoveryClient.getInstances(serviceId).get(0).getUri().toString() + "/swagger-dubbo/api-docs");
}
return HttpUtil.get(discoveryClient.getInstances(serviceId).get(0).getUri().toString() + "/v2/api-docs?group=" + HttpUtil.encodeUtf8(group));
}
风铃平台是新一代面向新移动化的基础开发平台,专门为开发者提供面向钉钉,企业微信,自己构建小程序,自己构建app以及开发平台的基础平台 无论是个人、团队、或是企业,都能够用风铃快速开发上线。