写这篇的目的是因为网上很多的博客跟着操作下来根本无法成功,跟着操作要么报404.要么报错Could not render e, see the console,最后没办法,自己研究了一波,最终成功了。
第一步:环境
这里我是用的是springboot 2.1.12 RELEASE版本,swagger用的是1.9.1.RELEASE,依赖如下,注册中心使用的是nacos,这里不在赘述nacos的搭建了,不明白的可以看我之前的文章centos7搭建nacos集群_取个昵称要人命的博客-CSDN博客
org.springframework.boot
spring-boot-dependencies
2.1.12.RELEASE
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery
2.1.0.RELEASE
org.springframework.cloud
spring-cloud-starter-gateway
2.1.0.RELEASE
com.spring4all
swagger-spring-boot-starter
1.9.1.RELEASE
第二步:搭建用户服务,实现微服务的自动注册和swagger访问
yml:
spring:
application:
name: qsbl-service-user
cloud:
nacos:
discovery:
server-addr: nacos的ip:nacos的端口
swagger配置类:
/**
* 如果是springsecurity项目的话
* 该配置类放在与启动类同一层,不然可能会报错找不到资源路径
*/
@Configuration
@EnableSwagger2
public class SwaggerConfig extends WebMvcConfigurationSupport {
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.qsbl.user.service.api.serviceimpl"))//改成自己接口在的包
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("用户服务接口")
.description("用户服务接口swagger")
.version("1.0")
.build();
}
/**
* 防止如果是springsecurity项目无法访问swagger
* @param registry
*/
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
// 解决静态资源无法访问
registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
// 解决swagger无法访问
registry.addResourceHandler("/swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
// 解决swagger的js文件无法访问
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
}
}
然后启动后访问:成功,用户服务的端口我这里是60001,成功访问get接口
第三步:搭建动态网关工程(利用mysql),并访问到user服务
这里不赘述数据库层的操作,相信增删改查的操作是都会的,我用的mysql+mybatis+tkmybatis
数据库表如下:
实体类:QsblRouteDO
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
@Accessors(chain = true)
@Table(name = "qsbl_route")
public class QsblRouteDO implements Serializable {
private static final long serialVersionUID = 2383801812375289673L;
@KeySql(genId = UUIdGenId.class)
@Id
private String id;//id
private String routeId;//路由id
private String routeName;//路由名称
private String routePattern;//设备匹配规则
private String routeType;//路由类型
private String routeUrl;//路由地址
private String status;//状态 0正常1失效
private String isDel;//是否删除 0否1是
}
mapper里添加接口:
@Select("select * from qsbl_route where status = '1' and is_del = '0'")
List getAllRoutes();
Service类:QsblRouteService(最重要)StripPrefix一定要加,不然就是各种报错
@Service
public class QsblRouteService implements ApplicationEventPublisherAware, CommandLineRunner {
private ApplicationEventPublisher applicationEventPublisher;
@Resource
private RouteDefinitionWriter routeDefinitionWriter;
@Resource
private QsblRouteMapper qsblRouteMapper;
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}
public void loadAllRoutes(){
List allRoutes = qsblRouteMapper.getAllRoutes();
System.out.println(allRoutes);
for (QsblRouteDO route : allRoutes) {
loadRoute(route);
}
}
public void loadRoute(QsblRouteDO qsblRouteDO) {
RouteDefinition definition = new RouteDefinition();
Map predicateParams = new HashMap<>(8);
PredicateDefinition predicate = new PredicateDefinition();
URI uri;
if(RouteTypeEnum.LB_TYPE.getValue().equals(qsblRouteDO.getRouteType())){
uri = UriComponentsBuilder.fromUriString("lb://"+qsblRouteDO.getRouteUrl()+"/").build().toUri();
}else {
uri = UriComponentsBuilder.fromHttpUrl(qsblRouteDO.getRouteUrl()).build().toUri();
}
//定义的路由唯一id
definition.setId(qsblRouteDO.getRouteId());
//路由转发地址
predicateParams.put("pattern",qsblRouteDO.getRoutePattern());
predicate.setName("Path");
predicate.setArgs(predicateParams);
// 名称是固定的, 路径去前缀
FilterDefinition filterDefinition = new FilterDefinition();
Map filterParams = new HashMap<>(8);
filterDefinition.setName("StripPrefix");
filterParams.put("_genkey_0", "1");
filterDefinition.setArgs(filterParams);
definition.setFilters(Arrays.asList(filterDefinition));
definition.setPredicates(Arrays.asList(predicate));
definition.setUri(uri);
routeDefinitionWriter.save(Mono.just(definition)).subscribe();
applicationEventPublisher.publishEvent(new RefreshRoutesEvent(this));
}
/**
* spring加载完成后自动执行
* @param args
* @throws Exception
*/
@Override
public void run(String... args) throws Exception {
loadAllRoutes();
}
}
yml
server:
port: 60010
spring:
application:
name: qsbl-cloud-gateway
cloud:
# gateway:
# routes:
# - id: user
# uri: lb://qsbl-service-lib/
# filters:
# - StripPrefix=1
# predicates:
# - Path=/user/**
# 加上下面的就会自动注册
# discovery:
# locator:
# enabled: true
nacos:
discovery:
server-addr: nacos的Ip:nacos的端口
启动类:
添加@EnableDiscoveryClient注解
然后启动后访问:成功,网关服务时60010端口,成功访问用户服务的get接口
第四步: 实现网关整合swagger并能访问到微服务的swagger
这一步也就是我开头说的网上很多的博客跟着操作下来根本无法成功,跟着操作要么报404.要么报错Could not render e, see the console的一步。
SwaggerHandler类:照抄就行
@RestController
@RequestMapping("/swagger-resources")
public class SwaggerHandler {
@Autowired(required = false)
private SecurityConfiguration securityConfiguration;
@Autowired(required = false)
private UiConfiguration uiConfiguration;
private final SwaggerResourcesProvider swaggerResources;
@Autowired
public SwaggerHandler(SwaggerResourcesProvider swaggerResources) {
this.swaggerResources = swaggerResources;
}
@GetMapping("/configuration/security")
public Mono> securityConfiguration() {
return Mono.just(new ResponseEntity<>(
Optional.ofNullable(securityConfiguration).orElse(SecurityConfigurationBuilder.builder().build()), HttpStatus.OK));
}
@GetMapping("/configuration/ui")
public Mono> uiConfiguration() {
return Mono.just(new ResponseEntity<>(
Optional.ofNullable(uiConfiguration).orElse(UiConfigurationBuilder.builder().build()), HttpStatus.OK));
}
@GetMapping("")
public Mono swaggerResources() {
return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK)));
}
}
SwaggerProvider类:(这里是和网上不一样的地方,重点就是这个get()方法,要能匹配到其他微服务的swagger链接,其实可以发现都一样,都是/v2/api-docs,无非就是在前面匹配一个微服务的id,数据库里查出来就好,和上面动态路由那块一样的道理)
@Component
public class SwaggerProvider implements SwaggerResourcesProvider {
@Resource
private QsblRouteMapper qsblRouteMapper;
@Override
public List get() {
List resources = new ArrayList<>();
List allRoutes = qsblRouteMapper.getAllRoutes();
for (QsblRouteDO route : allRoutes) {
resources.add(swaggerResource(route.getRouteName(), route.getRouteId()));
}
return resources;
}
private SwaggerResource swaggerResource(String routeName, String routeId) {
SwaggerResource swaggerResource = new SwaggerResource();
swaggerResource.setName(routeName);
swaggerResource.setLocation("/"+routeId+"/v2/api-docs");
swaggerResource.setSwaggerVersion("2.0");
return swaggerResource;
}
}
然后启动后访问:成功