Gateway动态路由

What?

前段时间买了个服务器,没怎么用,就跑了个在线获取IdeaCode的程序。使用率不怎么高,这次准备在跑一个Gateway网关,以后就把我所有的程序都接入到网关里。但是以前网关里的路由都是硬编码的形式写到配置文件里的,这就意味着我每发布一个程序都要重新打包部署一下网关。

程序猿的存在就是解决一些需要频繁操作的事件,所以要想办法解决硬编码路由的问题,所以我写了本篇Gateway动态路由。

思路

Gateway的路由配置有两种方式,一种是通过配置文件配置,一种是通过代码配置。我准备做一个类似于管理系统的系统来管理路由配置,所以要使用代码的方式配置路由。

在项目启动的时候从数据库读取配置并且存到Redis中。Gateway在初始化的时候从Redis中获取配置。

开发前的准备

  1. 一台电脑(废话,没有电脑怎么开发)
  2. Nacos注册中心(路由转发需要用到)
  3. MySQL数据库(持久化的储存路由配置)
  4. Redis(路由配置缓存)
  5. JDK1.8(我是基于JDK1.8做的开发)
  6. Maven(现在可是Maven的天下,总不能一个一个的添加依赖吧)

预览

Github:https://github.com/Ys2025/gateway-backend



Start

1、初始化数据库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
CREATE TABLE `gateway_route` (
`id` bigint NOT NULL,
`route_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
`uri` varchar(255) NOT NULL,
`predicates` varchar(255) NOT NULL,
`filters` varchar(255) DEFAULT NULL,
`ord` int DEFAULT '0',
`remarks` varchar(255) DEFAULT NULL,
`create_time` date DEFAULT NULL,
`update_time` date DEFAULT NULL,
`is_deleted` int DEFAULT '0',
`version` int DEFAULT '1',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

在这里插入图片描述

2、创建一个Gateway项目

启动类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package cn.yanghuisen.gateway;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@SpringBootApplication
@MapperScan("cn.yanghuisen.gateway.mapper")
@EnableDiscoveryClient
public class GatewayApplication {

public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}

}

pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.11.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>cn.yanghuisen</groupId>
<artifactId>gateway</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>gateway</name>
<description>Demo project for Spring Boot</description>

<properties>
<java.version>1.8</java.version>
<spring-cloud-alibaba.version>2.2.1.RELEASE</spring-cloud-alibaba.version>
<spring-cloud.version>Hoxton.SR9</spring-cloud.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-boot-starter -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>

application.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
server:
port: 8080
spring:
application:
name: GATEWAY
cloud:
nacos:
discovery:
server-addr: localhost:8848
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/gateway_route?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8
username: root
password: 123456
redis:
# Redis地址
host: localhost
# Redis端口
port: 6379
# RedisDB库
database: 0
# 链接超市
timeout: 10000ms
lettuce:
pool:
# 最大连接数
max-active: 1024
# 最大阻塞等待时间
max-wait: 10000ms
# 最大空闲时间
max-idle: 200
# 最小空闲链接
min-idle: 5
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
logic-delete-value: 1 # 逻辑已经删除的值(默认为1)
logic-not-delete-value: 0 # 逻辑没有删除的值(默认为0)

3、配置Redis

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package cn.yanghuisen.gateway.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
* @author admin
* @version 1.0
* @date 2020/5/14 20:52
* @Description 自定义模板
*/
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String,Object> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory){
// 创建模板
RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>();
// 设置String类型的Key的序列器
redisTemplate.setKeySerializer(new StringRedisSerializer());
// 设置String类型的value的序列器
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
// 设置Hash类型的Key的序列器
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
// 设置hash类型的value的序列器
redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
// 设置连接方式
redisTemplate.setConnectionFactory(lettuceConnectionFactory);
return redisTemplate;
}
}

4、自定义路由储存库(核心1)

此类用于从Redis获取路由配置信息,以及监听的路由保存和删除

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
package cn.yanghuisen.gateway.config;

import com.alibaba.fastjson.JSON;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionRepository;
import org.springframework.cloud.gateway.support.NotFoundException;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.ArrayList;
import java.util.List;

/**
* @author Y
* @date 2020/11/28 0:02
* @desc 自定义路由储存库
*/
@Component
public class RedisRouteDefinitionRepository implements RouteDefinitionRepository {


@Autowired
private RedisTemplate<String,Object> redisTemplate;

private static final String GATEWAY_ROUTES = "gateway:routes";


/**
* 获取路由信息,此处从Redis获取路由配置信息
* @return
*/
@Override
public Flux<RouteDefinition> getRouteDefinitions() {
List<RouteDefinition> routeDefinitions = new ArrayList<>();
// 获取Redis中配置的路由信息
List<Object> routes = redisTemplate.opsForHash().values(GATEWAY_ROUTES);
// 遍历路由
routes.forEach(route->{
// 把json反序列话为RouteDefinition类对象
RouteDefinition routeDefinition = JSON.parseObject(route.toString(), RouteDefinition.class);
routeDefinitions.add(routeDefinition);
});
return Flux.fromIterable(routeDefinitions);
}

/**
* 添加路由
* @param route
* @return
*/
@Override
public Mono<Void> save(Mono<RouteDefinition> route) {
return route.flatMap(routeDefinition -> {
// 把路由信息存到Redis中
redisTemplate.opsForHash().put(GATEWAY_ROUTES,routeDefinition.getId(), JSON.toJSONString(routeDefinition));
return Mono.empty();
});
}

/**
* 删除路由
* @param routeId
* @return
*/
@Override
public Mono<Void> delete(Mono<String> routeId) {
return routeId.flatMap(id -> {
// 判断redis中是否有该id的路由数据
if (redisTemplate.opsForHash().hasKey(GATEWAY_ROUTES,id)){
// 删除数据
redisTemplate.opsForHash().delete(GATEWAY_ROUTES,id);
return Mono.empty();
}
return Mono.defer(()-> Mono.error(new NotFoundException("Redis中没有该路由:"+id)));
});
}
}

5、SpringBoot启动配置(核心2)

此类用于在SpringBoot启动的时候从数据库读取路由配置信息,并且把信息储存到Redis中。以及发布路由的增删改事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
package cn.yanghuisen.gateway.handler;

import cn.yanghuisen.gateway.dto.GatewayRouteDTO;
import cn.yanghuisen.gateway.entity.GatewayRoute;
import cn.yanghuisen.gateway.mapper.GatewayRouteMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.util.UriComponentsBuilder;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
* @author Y
* @date 2020/11/28 0:38
* @desc
*/
@Slf4j
@Component
public class GatewayServiceHandler implements ApplicationEventPublisherAware, CommandLineRunner {

private static final String GATEWAY_ROUTES = "gateway:routes";

private ApplicationEventPublisher publisher;

@Autowired
private RouteDefinitionWriter routeDefinitionWriter;

@Autowired
private GatewayRouteMapper gatewayRouteMapper;

@Autowired
private RedisTemplate<String,Object> redisTemplate;

@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.publisher = applicationEventPublisher;
}

/**
* 项目启动的时候执行
* @param args
* @throws Exception
*/
@Override
public void run(String... args) throws Exception {
// 记载路由配置
this.loadRouteConfig();
}


public String loadRouteConfig(){
log.info("开始加载网关路由配置信息");
// 删除Redis里的路由配置信息
redisTemplate.delete(GATEWAY_ROUTES);
// 从数据库查询数据
List<GatewayRoute> gatewayRoutes = gatewayRouteMapper.selectList(null);
gatewayRoutes.forEach(gatewayRoute -> {
RouteDefinition definition = handleData(gatewayRoute);
// 保存路由
routeDefinitionWriter.save(Mono.just(definition)).subscribe();
});
// 发布事件,通知更新数据
this.publisher.publishEvent(new RefreshRoutesEvent(this));
return "success";
}

/**
* 保存路由
* @param dto
*/
public void saveRoute(GatewayRouteDTO dto){
GatewayRoute gatewayRoute = new GatewayRoute();
BeanUtils.copyProperties(dto,gatewayRoute);
// GatewayRoute转为RouteDefinition
RouteDefinition definition = handleData(gatewayRoute);
// 保存路由数据
routeDefinitionWriter.save(Mono.just(definition)).subscribe();
// 发布事件,通知更新数据
this.publisher.publishEvent(new RefreshRoutesEvent(this));
}

/**
* 更新路由
* @param dto
*/
public void updateRoute(GatewayRouteDTO dto){
GatewayRoute gatewayRoute = new GatewayRoute();
BeanUtils.copyProperties(dto,gatewayRoute);
// GatewayRoute转为RouteDefinition
RouteDefinition definition = handleData(gatewayRoute);
// 根据路由ID删除路由信息
routeDefinitionWriter.delete(Mono.just(dto.getOldRouteId())).subscribe();
// 重新保存路由数据
routeDefinitionWriter.save(Mono.just(definition)).subscribe();
// 发布事件,通知更新数据
this.publisher.publishEvent(new RefreshRoutesEvent(this));
}

/**
* 删除路由
* @param routeId
*/
public void deleteRoute(String routeId){
// 根据路由ID删除路由信息
routeDefinitionWriter.delete(Mono.just(routeId)).subscribe();
// 发布事件,通知更新数据
this.publisher.publishEvent(new RefreshRoutesEvent(this));
log.info("删除完毕");
}


/**
* GatewayRoute转RouteDefinition
* @param gatewayRoute
* @return
*/
public RouteDefinition handleData(GatewayRoute gatewayRoute){
RouteDefinition routeDefinition = new RouteDefinition();

URI uri = null;
// 判断Uri是不是http地址
if (gatewayRoute.getUri().startsWith("http")){
// http地址
uri = UriComponentsBuilder.fromHttpUrl(gatewayRoute.getUri()).build().toUri();
}else {
// 微服务服务名
uri = UriComponentsBuilder.fromUriString("lb://"+gatewayRoute.getUri()).build().toUri();
}
// 设置路由ID
routeDefinition.setId(gatewayRoute.getRouteId());
// 设置uri
routeDefinition.setUri(uri);
//谓语(路由转发条件)
PredicateDefinition predicate = new PredicateDefinition();
predicate.setName("Path");
Map<String,String> predicateArgs = new HashMap<>();
predicateArgs.put("pattern",gatewayRoute.getPredicates());
predicate.setArgs(predicateArgs);
routeDefinition.setPredicates(Collections.singletonList(predicate));

// 设置ord
if (null!=gatewayRoute.getOrd()){
routeDefinition.setOrder(gatewayRoute.getOrd());
}
return routeDefinition;
}

}

6、创建entity和DTO类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
package cn.yanghuisen.gateway.entity;

import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;

import java.util.Date;

/**
* @author Y
* @date 2020/11/28 1:16
* @desc 实体类
*/
@Data
public class GatewayRoute {

@TableId(type = IdType.ASSIGN_ID)
private String id;

private String routeId;

private String uri;

private String predicates;

private String filters;

private Integer ord;

private String remarks;

@TableField(fill = FieldFill.INSERT_UPDATE)
@JsonFormat(pattern = "yyyy-MM-dd")
private Date createTime;

@TableField(fill = FieldFill.INSERT_UPDATE)
@JsonFormat(pattern = "yyyy-MM-dd")
private Date updateTime;

/**
* 逻辑删除
*/
@TableLogic
private Integer isDeleted;

/**
* 乐观锁
*/
@Version
private Integer version;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package cn.yanghuisen.gateway.dto;

import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.util.Date;

/**
* @author Y
* @date 2020/11/28 1:16
* @desc DTO类
*/
@Data
public class GatewayRouteDTO {

private String id;

private String routeId;

private String oldRouteId;

private String uri;

private String predicates;

private String filters;

private Integer ord;

private String remarks;

@JsonFormat(pattern = "yyyy-MM-dd")
private Date createTime;

@JsonFormat(pattern = "yyyy-MM-dd")
private Date updateTime;

private Integer isDeleted;

private Integer version;
}

7、创建Service层接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package cn.yanghuisen.gateway.service;

import cn.yanghuisen.gateway.dto.GatewayRouteDTO;

import java.util.List;

/**
* @author Y
* @date 2020/11/28 22:38
* @desc
*/
public interface IRouteService {
/**
* 添加路由
* @param dto
* @return
*/
Integer add(GatewayRouteDTO dto);

/**
* 更新路由
* @param dto
* @return
*/
Integer update(GatewayRouteDTO dto);

/**
* 删除路由
* @param routeId
* @return
*/
Integer delete(String routeId);

/**
* 获取列表
* @return
*/
List<GatewayRouteDTO> list();
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package cn.yanghuisen.gateway.service.impl;

import cn.yanghuisen.gateway.dto.GatewayRouteDTO;
import cn.yanghuisen.gateway.entity.GatewayRoute;
import cn.yanghuisen.gateway.mapper.GatewayRouteMapper;
import cn.yanghuisen.gateway.service.IRouteService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

/**
* @author Y
* @date 2020/11/28 22:40
* @desc
*/
@Service
public class RouteServiceImpl implements IRouteService {

@Autowired
private GatewayRouteMapper routeMapper;

@Override
public Integer add(GatewayRouteDTO dto) {
GatewayRoute gatewayRoute = new GatewayRoute();
BeanUtils.copyProperties(dto,gatewayRoute);
return routeMapper.insert(gatewayRoute);
}

@Override
public Integer update(GatewayRouteDTO dto) {
GatewayRoute gatewayRoute = new GatewayRoute();
BeanUtils.copyProperties(dto,gatewayRoute);
return routeMapper.updateById(gatewayRoute);
}

@Override
public Integer delete(String routeId) {
QueryWrapper<GatewayRoute> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("route_id",routeId);
return routeMapper.delete(queryWrapper);
}

@Override
public List<GatewayRouteDTO> list() {
List<GatewayRouteDTO> result = new ArrayList<>();
List<GatewayRoute> gatewayRoutes = routeMapper.selectList(null);
gatewayRoutes.forEach(gatewayRoute -> {
GatewayRouteDTO dto = new GatewayRouteDTO();
BeanUtils.copyProperties(gatewayRoute,dto);
dto.setOldRouteId(gatewayRoute.getRouteId());
result.add(dto);
});

return result;
}
}

8、controller接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
package cn.yanghuisen.gateway.controller;

import cn.yanghuisen.gateway.dto.GatewayRouteDTO;
import cn.yanghuisen.gateway.handler.GatewayServiceHandler;
import cn.yanghuisen.gateway.service.IRouteService;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.util.List;

/**
* @author Y
* @date 2020/11/28 22:28
* @desc 路由API接口
*/
@RestController
@RequestMapping("/route")
@CrossOrigin
public class RouteController {

@Autowired
private GatewayServiceHandler gatewayServiceHandler;

@Autowired
private IRouteService routeService;

/**
* 刷新路由配置
* @return
*/
@GetMapping("/refresh")
public String refresh(){
return this.gatewayServiceHandler.loadRouteConfig();
}

/**
* 添加路由配置
* @param dto
* @return
*/
@PostMapping("/save")
public String add(@RequestBody GatewayRouteDTO dto){

if (StringUtils.isNotBlank(dto.getId())){
this.gatewayServiceHandler.updateRoute(dto);
this.routeService.update(dto);
}else {
this.gatewayServiceHandler.saveRoute(dto);
this.routeService.add(dto);
}
return "success";
}


@GetMapping("/delete")
public String delete(String routeId){
this.gatewayServiceHandler.deleteRoute(routeId);
this.routeService.delete(routeId);
return "success";
}

@GetMapping("/list")
public List<GatewayRouteDTO> list(){
return this.routeService.list();
}
}

9、前端代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
<template>
<div>
<el-row>
<el-button type="primary" icon="el-icon-circle-plus-outline" @click="addRoute">新增路由</el-button>
<el-button type="success" icon="el-icon-refresh" @click="refreshRoute">刷新路由</el-button>
</el-row>
<el-table
:data="tableData"
:row-class-name="tableRowClassName">
<el-table-column
prop="id"
label="ID"
show-overflow-tooltip>
</el-table-column>
<el-table-column
prop="routeId"
label="routeId"
show-overflow-tooltip>
</el-table-column>
<el-table-column
prop="uri"
label="uri"
show-overflow-tooltip>
</el-table-column>
<el-table-column
prop="predicates"
label="predicates"
show-overflow-tooltip>
</el-table-column>
<el-table-column
prop="filters"
label="filters"
show-overflow-tooltip>
</el-table-column>
<el-table-column
prop="ord"
label="order"
width="150">
</el-table-column>
<el-table-column
prop="remarks"
label="remarks"
width="150">
</el-table-column>
<el-table-column
prop="createTime"
label="createTime"
width="150">
</el-table-column>
<el-table-column
prop="updateTime"
label="updateTime"
width="150">
</el-table-column>
<el-table-column
label="操作"
fixed="right"
width="150">
<template slot-scope="scope">
<el-button
size="mini"
@click="handleEdit(scope.row)">编辑</el-button>
<el-button
size="mini"
type="danger"
@click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>

<el-dialog :title="dialogTitle" :visible.sync="dialogFormVisible">
<el-form :model="form" label-width="100px">
<el-form-item label="RouteId">
<el-input v-model="form.routeId"></el-input>
</el-form-item>
<el-form-item label="Uri">
<el-input v-model="form.uri"></el-input>
</el-form-item>
<el-form-item label="Predicates">
<el-input v-model="form.predicates"></el-input>
</el-form-item>
<el-form-item label="Filters">
<el-input v-model="form.filters"></el-input>
</el-form-item>
<el-form-item label="Order">
<el-input v-model="form.ord"></el-input>
</el-form-item>
<el-form-item label="Remarks">
<el-input type="textarea" v-model="form.remarks"></el-input>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogFormVisible = false">取 消</el-button>
<el-button type="primary" @click="clickDialog(form)">确 定</el-button>
</div>
</el-dialog>
</div>
</template>

<script>
export default {
data () {
return {
tableData: [],
dialogFormVisible: false,
form: {},
dialogTitle: '新增路由'
}
},
methods:{
tableRowClassName(data) {
console.log(data)
if (data.rowIndex%2){
return "warning-row"
}else {
return "success-row"
}
},
handleEdit(data) {
console.log(data);
this.dialogTitle='编辑路由'
this.dialogFormVisible = true
this.form = data
},
handleDelete(data) {
console.log(data);
this.$axios.get('http://localhost:8080/route/delete', {
params: {
routeId: data.routeId
}
}).then(result=> {
console.log(result);
this.getList()
})
},
clickDialog(data){
console.log(data)
this.$axios.post('http://localhost:8080/route/save', this.form).then(result=> {
console.log(result);
this.dialogFormVisible = false
this.getList()
})
},
refreshRoute(){
this.$axios.get('http://localhost:8080/route/refresh').then(result => {
console.log(result.data);
this.getList()
})
},
getList(){
this.$axios.get('http://localhost:8080/route/list').then(result => {
console.log(result.data);
this.tableData = result.data
})
},
addRoute(){
this.dialogTitle='新增路由'
this.dialogFormVisible = true
this.form = {}
}
},
created() {
console.log(1111)
this.getList()
}
}
</script>

<style>
.el-table .warning-row {
background: oldlace;
}

.el-table .success-row {
background: #f0f9eb;
}
</style>


时间原因,代码中没做必填校验。filters暂时没用到,也没写。有空再补充吧。