SpringCloud-Gateway配置及持久化、过滤器、异常处理

news/2025/1/11 4:00:08/

文章目录

    • yml配置
    • 代码配置
    • 持久化
    • 数据结构
    • predicates(断言) 和filters(过滤)新增配置说明
    • 相关接口
    • 全局过滤器
    • 局部过滤器
    • 全局异常处理

gateway不能和web一起使用 需要排除掉

        <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId></dependency>

解决冲突

yml配置

spring:main:allow-circular-references: true #解决循环依赖,暂时跳过application:name: gateway-servercloud:# https://cloud.tencent.com/developer/article/1650115?from=15425gateway:routes:# 消息服务- id: message-server# 匹配后路径 配合nacos服务名称uri: lb://message-serverpredicates:# 断言路劲,匹配成功后就走uri,多个用逗号分隔- Path=/api/msg/**#- After=2020-03-08T10:59:34.102+08:00[Asia/Shanghai] 在什么时间段之前才匹配#- Cookie=username,zhangshuai #并且Cookie是username=zhangshuai才能访问#- Header=X-Request-Id, \d+ #请求头中要有X-Request-Id属性并且值为整数的正则表达式#- Host=**.tecloman.cn 主机名相同才能转发#- Method=GET  请求方法匹配#- Query=username, \d+ #要有参数名称并且是正整数才能路由# 计算服务- id: computer-serveruri: lb://computer-serverpredicates:- Path=/energyStorageStation/**# web服务- id: hss-server#有多个hss-server服务,测试连不上生产的数据库,要超时报错#uri: lb://hss-serveruri: http://localhost:1000predicates:- Path=/api/swagger/**,/api/hss/**,/api/ruralGrid/**#- Path=[/api/hss/**,/api/sys/**,/api/admin/**,/api/app/**,/api/openApi/**,/api/distributed/**,/api/ezviz/**,/api/ruralGrid/**,/api/swagger/**]# 全局的跨域处理globalcors:add-to-simple-url-handler-mapping: true # 解决options请求被拦截问题corsConfigurations:'[/**]': # 哪些访问地址做跨域处理allowedOrigins: # 允许哪些网站的跨域请求- "http://localhost:8090"allowedMethods: # 允许的跨域ajax的请求方式- "GET"- "POST"- "DELETE"- "PUT"- "OPTIONS"allowedHeaders: "*" # 允许在请求中携带的头信息allowCredentials: true # 是否允许携带cookiemaxAge: 360000  # 这次跨域检测的有效期

使用 uri: http://localhost:1000 指定网址使用,打包到linux服务器,直接使用docker部署会出问题,docker容器中不能使用localhost,服务运行后应查询到IP地址后再修改gateway的配置再部署

docker inspect --format '{{ .NetworkSettings.IPAddress }}' <container-ID>
或
docker inspect <container id> 

代码配置

package gateway.server.config;import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;import javax.servlet.http.HttpServletRequest;/***网关接口路由配置*@author chens*@date  2022-12-05更新*/public class GatewayConfig {@Beanpublic RouteLocator customRouteLocator(RouteLocatorBuilder builder) {// 通信服务的接口,因为包含在web服务里面,有点特殊return builder.routes().route("hss-server", r -> r.path(// path最好不要写死,"/api/captcha.jpg/**","/api/ruralGrid/**","/api/test/**")// 使用order来处理接口包含关系,web服务包含全部接口,// 其它服务无法处理的情况,web服务最后执行.and().order(0)// 有多个重名的服务,本地连不上生产数据库,最好采用http方式//.uri(url).uri("lb://cs-test-hss-server"))// 消息服务.route("message-server", r -> r.path("/api/msg/**").uri("lb://message-server"))// 运算服务.route("computer-server", r -> r.path("/energyStorageStation/**").uri("lb://computer-server"))// 通信服务.route("communications-server", r -> r.path("/api/hss/classify/**","/api/hss/type/**","/api/hss/strategy/**","/api/hss/protocol/**","/api/sys/script/**")// 在web服务前面先执行,.and().order(1).uri("lb://communications-server")).build();}//跨域配置@Beanpublic CorsWebFilter corsWebFilter(){UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();CorsConfiguration configuration = new CorsConfiguration();// 配置跨域的信息configuration.addAllowedHeader("*");configuration.addAllowedMethod("*");// SpringBoot升级到2.4.0 之后需要使用该配置configuration.addAllowedOriginPattern("*");configuration.setAllowCredentials(true);source.registerCorsConfiguration("/**",configuration);return new CorsWebFilter(source);}
}

uri: lb://computer-server 采用服务名转发 需引入

        <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-loadbalancer</artifactId></dependency>

持久化

访问端点需要引入

        <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency>
management:endpoints:web:base-path: /root #根路劲 默认actuatorexposure:include: "*" #暴露所有接口# server:#这和服务端口一样 那就没法走路由,过滤器不会生效# port: 8888

查看路由节点
localhost:8090/root/gateway/routes
在这里插入图片描述gateway提供的类GatewayControllerEndpoint 包含了crud的接口
gateway/routes 就是其中一个接口
现在我们自己写crud,因为gateway操作的全是内存上的数据,现在需要把数据存入数据库,项目启动从数据库读取配置
建表SQL

CREATE TABLE `gateway_route` (`id` INT NOT NULL AUTO_INCREMENT,`name` VARCHAR ( 255 ) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '路由名称',`route_id` VARCHAR ( 255 ) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '路由key',`uri` VARCHAR ( 255 ) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '转发URL',`predicates` json NOT NULL COMMENT '断言数据',`filters` json NOT NULL COMMENT '过滤数据',`order_num` INT DEFAULT NULL COMMENT '顺序',`state` TINYINT ( 1 ) NOT NULL DEFAULT '0' COMMENT '是否启用 0未启用 1启用',`remark` VARCHAR ( 255 ) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '备注',`create_time` datetime NOT NULL COMMENT '创建时间',`create_user_id` BIGINT NOT NULL COMMENT '创建人ID',`dtime` bit ( 1 ) NOT NULL DEFAULT b '0' COMMENT '逻辑删除标记',
PRIMARY KEY ( `id` ) USING BTREE 
) ENGINE = INNODB AUTO_INCREMENT = 17 DEFAULT CHARSET = utf8mb3 ROW_FORMAT = DYNAMIC COMMENT = '<dodo-server-app-manager>上架应用路由信息表';

持久框架 MP autoResultMap json字段自动映射

package gateway.server.hss.entity;import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import lombok.Data;import java.io.Serializable;
import java.util.Date;/*** 网关路由配置* @author chens*/
@Data
@TableName(value = "gateway_route", autoResultMap = true)
public class GatewayRouterEntity extends Model<GatewayRouterEntity> implements Serializable {private static final long serialVersionUID = 1L;@TableIdprivate Integer id;/*** 路由名称*/private String name;/*** 路由key*/private String routeId;/*** 转发URL*/private String uri;/*** 断言数据*/@TableField(typeHandler = JacksonTypeHandler.class)private JSONArray predicates;/*** 过滤数据*/@TableField(typeHandler = JacksonTypeHandler.class)private JSONArray filters;/*** 备注*/private String remark;/*** 执行顺序*/private int orderNum = 0;/*** 状态 0未启用 1启用*/private int state = 0;/*** 创建人ID*/private Long createUserId;@TableField(exist = false)private String createUserName;private Date createTime;private int dtime = 0;
}

gateway只能使用ServerWebExchange 获取请求信息 ,不能使用HttpServletRequest
ServerWebExchange exchange

package gateway.server.hss.controller;import gateway.server.util.PageUtils;
import gateway.server.util.R;
import gateway.server.hss.entity.GatewayRouterEntity;
import gateway.server.hss.service.GatewayRouteService;
import gateway.server.hss.service.impl.DynamicRouteService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.server.ServerWebExchange;import javax.servlet.http.HttpServletRequest;
import java.util.Map;/*** 网关路由配置** @author chens* @create 2022-12-7* @desc**/
//@Component
//@RestControllerEndpoint(id = "chen")
@RestController
@RequestMapping("/gateway/route")
public class GatawayRouteController {@Autowiredprivate GatewayRouteService gatewayRouteService;private final DynamicRouteService dynamicRouteService;public GatawayRouteController(DynamicRouteService dynamicRouteService) {this.dynamicRouteService = dynamicRouteService;}@GetMapping("/list")public R list(ServerWebExchange exchange, Map<String, Object> params) {PageUtils page = gatewayRouteService.queryPage(params);return R.ok().put("page", page);}@PostMapping("/save")public R save(@RequestBody GatewayRouterEntity entity, ServerWebExchange exchange) {return this.dynamicRouteService.save(entity);}@PostMapping("/update")public R update(ServerWebExchange exchange, @RequestBody GatewayRouterEntity entity) {return this.dynamicRouteService.update(entity);}/*** 修改路由状态** @param routeId 路由Id* @return*/@PostMapping("/upState/{routeId}")public R upState(ServerWebExchange exchange,@PathVariable("routeId") String routeId) {return this.dynamicRouteService.upState(routeId);}/*** 删除路由** @param routeId 路由Id* @return*/@PostMapping("/delete/{routeId}")public R delete(ServerWebExchange exchange, @PathVariable("routeId") String routeId) {return this.dynamicRouteService.delete(routeId);}/*** 刷新路由** @return*/@GetMapping("/flush")public R flush(ServerWebExchange exchange) {return this.dynamicRouteService.flushRoute();}}

主要CRUD类

package gateway.server.hss.service.impl;import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import gateway.server.util.R;
import gateway.server.hss.entity.GatewayRouterEntity;
import lombok.extern.log4j.Log4j2;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.filter.FilterDefinition;
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.stereotype.Service;
import org.springframework.web.util.UriComponentsBuilder;
import reactor.core.publisher.Mono;import java.net.URI;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;/*** @desc 动态路由配置**/
@Service
@Log4j2
public class DynamicRouteService implements ApplicationEventPublisherAware, ApplicationRunner {private final RouteDefinitionWriter routeDefinitionWriter;private GatewayRouteServiceImpl gatewayRouteServiceImpl;private ApplicationEventPublisher publisher;public DynamicRouteService(RouteDefinitionWriter routeDefinitionWriter, GatewayRouteServiceImpl gatewayRouteServiceImpl) {this.routeDefinitionWriter = routeDefinitionWriter;this.gatewayRouteServiceImpl = gatewayRouteServiceImpl;}/*** 增加路由** @param gatewayRouterEntity* @return*/public R save(GatewayRouterEntity gatewayRouterEntity) {GatewayRouterEntity one = getOne(gatewayRouterEntity.getRouteId());if (one != null) return R.error("路由ID已存在");RouteDefinition definition = convertGateway(gatewayRouterEntity);// 新增到内存中, 新增先暂不写入内存,更改状态再写入//routeDefinitionWriter.save(Mono.just(definition)).subscribe();// 保存到数据库中saveData(gatewayRouterEntity.getName(), gatewayRouterEntity.getRemark(), definition);flushRouteConfig();return R.ok();}/*** 修改路由状态*/public R upState(String routeId) {GatewayRouterEntity entity = getOne(routeId);// 状态取反,判断状态 决定删除内存中的路由还是新增到内存中if (entity == null) return R.error("路由不存在");RouteDefinition definition = convertGateway(entity);if (entity.getState() == 0) {entity.setState(1);// 启用 新增到内存中routeDefinitionWriter.save(Mono.just(definition)).subscribe();} else if (entity.getState() == 1) {entity.setState(0);// 关闭 从内存中删除routeDefinitionWriter.delete(Mono.just(definition.getId())).subscribe();} else return R.error("路由状态异常");flushRouteConfig();// 状态取反 更新到数据库gatewayRouteServiceImpl.updateById(entity);return R.ok();}/*** 更新路由** @param routeForm* @return*/public R update(GatewayRouterEntity routeForm) {GatewayRouterEntity one = getOne(routeForm.getRouteId());if (one != null && !routeForm.getId().equals(one.getId()) ) {return R.error("路由ID已存在");}RouteDefinition definition = convertGateway(routeForm);try {routeDefinitionWriter.delete(Mono.just(definition.getId())).subscribe();} catch (Exception e) {return R.error("未知路由信息");}try {routeDefinitionWriter.save(Mono.just(definition)).subscribe();saveData(routeForm.getName(), routeForm.getRemark(), definition);flushRouteConfig();return R.ok();} catch (Exception e) {return R.error("路由信息修改失败!");}}/*** 删除路由** @param routeId 路由ID* @return*/public R delete(String routeId) {this.routeDefinitionWriter.delete(Mono.just(routeId)).subscribe();gatewayRouteServiceImpl.remove(new QueryWrapper<GatewayRouterEntity>().lambda().eq(GatewayRouterEntity::getRouteId, routeId));flushRouteConfig();return R.ok();}/*** 刷新路由** @return*/private void flushRouteConfig() {this.publisher.publishEvent(new RefreshRoutesEvent(this));}public R flushRoute() {flushRouteConfig();return R.ok();}@Overridepublic void setApplicationEventPublisher(ApplicationEventPublisher publisher) {this.publisher = publisher;}@Overridepublic void run(ApplicationArguments args) {log.info("----------从数据库加载额外路由信息---------");this.queryRoute();}// 从数据库查询配置private void queryRoute() {List<RouteDefinition> gatewayList = gatewayRouteServiceImpl.List();log.info("----------数据库路由数量:{}---------", gatewayList.size());// 数据库中的配置写入内存中gatewayList.forEach(x -> routeDefinitionWriter.save(Mono.just(x)).subscribe());flushRouteConfig();}/*** 实体转换成gateway实体** @param entity* @return*/private RouteDefinition convertGateway(GatewayRouterEntity entity) {RouteDefinition definition = new RouteDefinition();definition.setId(entity.getRouteId());definition.setOrder(entity.getOrderNum());//设置断言List<PredicateDefinition> predicateDefinitions = entity.getPredicates().stream().distinct().map(x -> {PredicateDefinition predicate = new PredicateDefinition();Map object = (Map) x;predicate.setArgs((Map) object.get("args"));predicate.setName(object.get("name").toString());return predicate;}).collect(Collectors.toList());definition.setPredicates(predicateDefinitions);// 设置过滤List<FilterDefinition> filterList = entity.getFilters().stream().distinct().map(x -> {FilterDefinition filter = new FilterDefinition();Map object = (Map) x;filter.setArgs((Map) object.get("args"));filter.setName(object.get("name").toString());return filter;}).collect(Collectors.toList());definition.setFilters(filterList);// 设置URI,判断是否进行负载均衡URI uri;if (entity.getUri().startsWith("http")) {uri = UriComponentsBuilder.fromHttpUrl(entity.getUri()).build().toUri();} else {uri = URI.create(entity.getUri());}definition.setUri(uri);return definition;}/*** 数据落库*/public void saveData(String name, String remark, RouteDefinition definition) {String routeId = definition.getId();List<PredicateDefinition> predicates = definition.getPredicates();List<FilterDefinition> filters = definition.getFilters();int order = definition.getOrder();URI uri = definition.getUri();GatewayRouterEntity entity = new GatewayRouterEntity();entity.setName(name);entity.setRouteId(routeId);entity.setUri(uri.toString());entity.setPredicates(JSONArray.parseArray(JSON.toJSONString(predicates)));entity.setFilters(JSONArray.parseArray(JSON.toJSONString(filters)));entity.setRemark(remark);entity.setOrderNum(order);entity.setCreateUserId(1L);entity.setCreateTime(new Date());entity.setDtime(0);// 数据库不存在则保存,存在修改路由Id保存GatewayRouterEntity one = getOne(routeId);if (one == null) {gatewayRouteServiceImpl.save(entity);} else {entity.setId(one.getId());gatewayRouteServiceImpl.updateById(entity);}}public GatewayRouterEntity getOne(String routeId) {GatewayRouterEntity entity = gatewayRouteServiceImpl.getOne(new QueryWrapper<GatewayRouterEntity>().lambda().eq(GatewayRouterEntity::getRouteId, routeId).last("limit 1"), false);return entity;}}

查询类

package gateway.server.hss.service.impl;import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import gateway.server.util.PageUtils;
import gateway.server.util.Query;
import gateway.server.hss.entity.GatewayRouterEntity;
import gateway.server.hss.dao.RouteMapper;
import gateway.server.hss.service.GatewayRouteService;
import org.springframework.cloud.gateway.filter.FilterDefinition;
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.stereotype.Service;import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;/*** @create 2022-12-7 14:13* @desc 路由信息持久化**/
@Service
public class GatewayRouteServiceImpl extends ServiceImpl<RouteMapper, GatewayRouterEntity> implements GatewayRouteService {@Overridepublic PageUtils queryPage(Map<String, Object> params) {IPage page = this.page(new Query<GatewayRouterEntity>().getPage(params),new QueryWrapper<GatewayRouterEntity>().eq("dtime", 0));return new PageUtils(page);}public List<RouteDefinition> List() {// 只查询启用状态的配置List<GatewayRouterEntity> list = list(new QueryWrapper<GatewayRouterEntity>().lambda().eq(GatewayRouterEntity::getState, 1).eq(GatewayRouterEntity::getDtime, 0));return list.stream().map(x -> {RouteDefinition routeDefinition = new RouteDefinition();routeDefinition.setId(x.getRouteId());// 这里需要注意判空routeDefinition.setPredicates(JSONObject.parseArray(x.getPredicates().toJSONString(), PredicateDefinition.class));routeDefinition.setFilters(JSONObject.parseArray(x.getFilters().toJSONString(), FilterDefinition.class));try {routeDefinition.setUri(new URI(x.getUri()));routeDefinition.setOrder(x.getOrderNum());routeDefinition.setMetadata(new HashMap<>(2));return routeDefinition;} catch (URISyntaxException e) {return null;}}).filter(Objects::nonNull).collect(Collectors.toList());}}

新增和删除路由逻辑类

package gateway.server.hss.service.impl;import lombok.extern.log4j.Log4j2;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionRepository;
import org.springframework.cloud.gateway.support.NotFoundException;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;/*** @author Administrator* @create 2022-12-07* @desc 自定义内存路由管理仓,开启日志打印**/
@Service
@Log4j2
public class DiyRouteDefinitionRepository implements RouteDefinitionRepository {public  final Map<String, RouteDefinition> routes = Collections.synchronizedMap(new LinkedHashMap<>());@Overridepublic Flux<RouteDefinition> getRouteDefinitions() {Collection<RouteDefinition> values = routes.values();return Flux.fromIterable(values);}@Overridepublic Mono<Void> save(Mono<RouteDefinition> route) {return route.flatMap( r -> {log.info("新增路由信息:{}",r);routes.put(r.getId(), r);return Mono.empty();});}@Overridepublic Mono<Void> delete(Mono<String> routeId) {return routeId.flatMap(id -> {log.info("删除路由信息,路由ID为:{}",id);if (routes.containsKey(id)) {routes.remove(id);return Mono.empty();}return Mono.defer(() -> Mono.error(new NotFoundException("RouteDefinition not found: "+routeId)));});}}

数据结构


{"name":"通信路由","routeId": "communications-server","uri": "lb://communications-server","order": 1,"predicates": [{"name": "Path","args": {"_genkey_0": "/api/hss/classify/**","_genkey_1": "/api/hss/type/**","_genkey_2": "/api/hss/strategy/**","_genkey_3": "/api/hss/protocol/**","_genkey_4": "/api/sys/script/**"}}],"remark":"测试自定义路由信息","filters": [{"name": "StripPrefix","args": {"_genkey_0": "1"}}]}

predicates(断言) 和filters(过滤)新增配置说明


predicates, name和args固定key不可更改,首字母大写 ,args参数对象,_genkey_*代表其中一个参数,固定格式

name含义示例
Path指定路径匹配{“name”:“Path”,“args”:{“pattern”:“/aa/“,“pattern1”:”/bb/”}}
Cookie配置对Cookie中值的匹配,第一个为key,第二个为value{“name”:“Cookie”,“args”:{“_genkey_0”:“chocolate”,“_genkey_1”:“ch.p”}}
Header匹配Http请求中设置的内容{“name”:“Header”,“args”:{“_genkey_0”:“X-Request-Id”,“_genkey_1”:“\d+”}}
Host匹配Http请求Host,匹配所有host为**.tecloman.cn的请求{“name”:“Host”,“args”:{“_genkey_0”:“**.somehost.com”}}
Method匹配Http请求头{“name”:“Method”,“args”:{“_genkey_0”:“GET”}}
Query匹配Http请求中的查询参数,请求中携带{“name”:“Query”,“args”:{“_genkey_0”:“param1”,“_genkey_1”:“value”}}
RemoteAddr匹配请求中的RemoteAddr{“name”:“RemoteAddr”,“args”:{“_genkey_0”:“192.168.1.1/24”}}
After设置时间之后可以访问{“name”:“After”,“args”:{“_genkey_0”:“2017-01-20T17:42:47.789-07:00[America/Denver]”}}
Before设置时间之前可以访问{“name”:“Before”,“args”:{“_genkey_0”:“2017-01-20T17:42:47.789-07:00[America/Denver]”}}
Between设置时间段内可以访问{“name”:“Between”,“args”:{“_genkey_0”:“2017-01-20T17:42:47.789-07:00[America/Denver]”,“_genkey_1”:“2017-01-21T17:42:47.789-07:00[America/Denver]”}}
Weight两组以上路由可以配置权重路由{“name”:“Weight”,“args”:{“_genkey_0”:“service1”,“_genkey_1”:“80”}}

filters name 属性

name含义示例
RewritePath路径重写{“name”:“RewritePath”,“args”:{“_genkey_0”:“/foo/(?.*)”,“_genkey_1”:“/${segment}”}}
AddRequestHeader#### 修改请求头{“name”:“AddRequestHeader”,“args”:{“_genkey_0”:“X-Request-Foo”,“_genkey_1”:“Bar”}}
AddRequestParameter修改请求参数{“name”:“AddRequestParameter”,“args”:{“_genkey_0”:“foo”,“_genkey_1”:“bar”}}
AddResponseHeader修改响应参数{“name”:“AddResponseHeader”,“args”:{“_genkey_0”:“X-Request-Foo”,“_genkey_1”:“Bar”}}
PrefixPath路径前缀增强{“name”:“PrefixPath”,“args”:{“_genkey_0”:“/mypath”}}
StripPrefix路径前缀删除{“name”:“StripPrefix”,“args”:{“_genkey_0”:“2”}}
PreserveHostHeader请求携带保留原始Host{“name”:“PreserveHostHeader”,“args”:{}}
RedirectTo重定向{“name”:“RedirectTo”,“args”:{“_genkey_0”:“302”,“_genkey_1”:“http://acme.org”}}
Hystrix断路器{“name”:“Hystrix”,“args”:{“name”:“fallbackcmd”,“fallbackUri”:“forward:/incaseoffailureusethis”}}
RequestRateLimiter集成Redis原生支持请求限流{“name”:“RequestRateLimiter”,“args”:{“redis-rate-limiter.replenishRate”:“10”,“redis-rate-limiter.burstCapacity”:“20”}}
RemoveRequestHeader删除请求头属性{“name”:“RemoveRequestHeader”,“args”:{“_genkey_0”:“X-Request-Foo”}}
RemoveResponseHeader删除响应头属性{“name”:“RemoveResponseHeader”,“args”:{“_genkey_0”:“X-Request-Foo”}}
RewriteResponseHeader重写响应头{“name”:“RewriteResponseHeader”,“args”:{“_genkey_0”:“X-Response-Foo”,“_genkey_1”:“password=[^&]+”,“_genkey_2”:“password=***”}}
SetPath重设请求路径{“name”:“SetPath”,“args”:{“_genkey_0”:“/{segment}”}}
SetResponseHeader设置响应头{“name”:“SetResponseHeader”,“args”:{“_genkey_0”:“X-Response-Foo”,“_genkey_1”:“Bar”}}
SetStatus设置Http状态{“name”:“SetStatus”,“args”:{“_genkey_0”:“302”}}
RequestSize设置文件传输大小{“name”:“RequestSize”,“args”:{“_genkey_0”:“5000000”}}
Retry失败重试{“name”:“Retry”,“args”:{“_genkey_0”:3,“_genkey_1”:“BAD_GATEWAY”}}

相关接口


列表GETlocalhost:8090/gateway/route/list
刷新GETlocalhost:8090/gateway/route/flush
新增POSTlocalhost:8090/gateway/route/save
修改POSTlocalhost:8090/gateway/route/update
删除POSTlocalhost:8090/gateway/route/delete/{routeId}
修改状态POSTlocalhost:8090/gateway/route/upState/{routeId}
删除传入ID为路由的ID,修改状态传入为主键ID
网关端点localhost:8090/root/gateway/
查看正在运行的路由信息localhost:8090/root/gateway/routes
监控端点localhost:8090/root/

全局过滤器

经过网关路由转发 才能走到全局过滤器

package gateway.server.filter;import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import gateway.server.util.R;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;import java.nio.charset.StandardCharsets;/*** @author Administrator* @create 2022-12-07 17:19* @desc 全局过滤器*/
@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {@Value("${custom.version}")String version;@Value("${custom.enable}")Boolean enable;@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {if (!enable) return chain.filter(exchange);// 获取前端传入的versionServerHttpRequest request = exchange.getRequest();String versionNum = request.getHeaders().getFirst("version");if (version.equals(versionNum)) {//当前放行,交由下个过滤器过滤return chain.filter(exchange);} else {String path = request.getURI().getPath();if ("/api/sys/login".equals(path)) {return chain.filter(exchange);}// 版本不一致,返版本回前端 强制刷新byte[] bytes = JSON.toJSONString(R.error(426, "error").put("version", version), SerializerFeature.WriteMapNullValue).getBytes(StandardCharsets.UTF_8);ServerHttpResponse response = exchange.getResponse();response.getHeaders().add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);DataBuffer buffer = response.bufferFactory().wrap(bytes);//响应出去return response.writeWith(Flux.just(buffer));}}@Overridepublic int getOrder() {return -1;}
}

局部过滤器

处理某个服务转发 配置文件中需要指定

    gateway:routes:- id: web_routeuri: lb://web-serverpredicates:- Path=/api/captcha.jpg/**,/api/ruralGrid/**filters:- AddResponseHeader=name,tecloman #添加响应头- Local #局部过滤器的前缀 LocalGatewayFilter  只取Local就行

注册局部过滤器,全局的不需要

package gateway.server.filter;import gateway.server.filter.LocalGatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.stereotype.Component;/*** @author Administrator*/
@Component
public class LocalGatewayFilterFactory extends AbstractGatewayFilterFactory<Object> {// 注册局部过滤器@Overridepublic GatewayFilter apply(Object config) {return new LocalGatewayFilter();}
}
package gateway.server.filter;import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import gateway.server.util.R;
import gateway.server.util.SpringUtil;
import gateway.server.hss.dao.SysUserTokenDao;
import gateway.server.hss.entity.SysUserTokenEntity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;import java.nio.charset.StandardCharsets;/*** @author chens* @create 2022-12-07 17:19* @desc 局部过滤器* 主要处理gateway本服务的接口和端点,其它服务绕过**/
@Component
public class LocalGatewayFilter implements GatewayFilter, Ordered {//private static SysUserTokenDao sysUserTokenDao = SpringUtil.getBean(SysUserTokenDao.class);@Autowiredprivate SysUserTokenDao sysUserTokenDao;// 多个过滤器,决定顺序@Overridepublic int getOrder() {return -1;}// 超级管理员才允许操作gateway相关接口 ,需要路由转发才生效@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {HttpHeaders headers = exchange.getRequest().getHeaders();String token = headers.getFirst("token");SysUserTokenEntity entity = sysUserTokenDao.selectById(1);String msg = "无权限查看此接口";if (entity != null) {if (entity.getToken().equals(token)) {if (entity.getExpireTime() > System.currentTimeMillis() / 1000) {return chain.filter(exchange);} else {msg = "token失效,请重新登录";}}}byte[] bytes = JSON.toJSONString(R.error(401, msg), SerializerFeature.WriteMapNullValue).getBytes(StandardCharsets.UTF_8);ServerHttpResponse response = exchange.getResponse();response.getHeaders().add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);DataBuffer buffer = response.bufferFactory().wrap(bytes);//响应出去return response.writeWith(Flux.just(buffer));}
}

全局异常处理

不能像spring boot那样类上打个@RestControllerAdvice注解使用

package gateway.server.config;import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import gateway.server.util.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.NestedExceptionUtils;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebExceptionHandler;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;/*** @author Administrator* gateway全局异常处理*/
@Slf4j
@Order(-1)
@Component
public class CustomWebExceptionHandler implements WebExceptionHandler {private static final Set<String> DISCONNECTED_CLIENT_EXCEPTIONS;//排除部份系统级的异常static {Set<String> exceptions = new HashSet<>();exceptions.add("AbortedException");exceptions.add("ClientAbortException");exceptions.add("EOFException");exceptions.add("EofException");DISCONNECTED_CLIENT_EXCEPTIONS = Collections.unmodifiableSet(exceptions);}@Overridepublic Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {if (exchange.getResponse().isCommitted() || isDisconnectedClientError(ex)) {return Mono.error(ex);}ServerHttpRequest request = exchange.getRequest();String rawQuery = request.getURI().getRawQuery();String query = StringUtils.hasText(rawQuery) ? "?" + rawQuery : "";String path = request.getPath() + query;String message;HttpStatus status = determineStatus(ex);if (status == null) {status = HttpStatus.INTERNAL_SERVER_ERROR;}// 通过状态码自定义异常信息if (status.value() >= 400 && status.value() < 500) {message = "路由服务不可达或禁止访问!";} else {message = "路由服务异常!" + ex.getMessage();}message += " path:" + path;//工具类输出json字符串byte[] bytes = JSON.toJSONString(R.error(status.value(), message), SerializerFeature.WriteMapNullValue).getBytes(StandardCharsets.UTF_8);ServerHttpResponse response = exchange.getResponse();response.getHeaders().add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);DataBuffer buffer = response.bufferFactory().wrap(bytes);//响应出去return response.writeWith(Flux.just(buffer));}@Nullableprotected HttpStatus determineStatus(Throwable ex) {if (ex instanceof ResponseStatusException) {return ((ResponseStatusException) ex).getStatus();}return null;}private boolean isDisconnectedClientError(Throwable ex) {return DISCONNECTED_CLIENT_EXCEPTIONS.contains(ex.getClass().getSimpleName())|| isDisconnectedClientErrorMessage(NestedExceptionUtils.getMostSpecificCause(ex).getMessage());}private boolean isDisconnectedClientErrorMessage(String message) {message = (message != null) ? message.toLowerCase() : "";return (message.contains("broken pipe") || message.contains("connection reset by peer"));}
}

如果是在gateway本服务写的接口还需要添加跨域配置

package gateway.server.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.cors.reactive.CorsUtils;import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;/*** 本地接口的跨域配置*/
@Configuration
public class CorsConfig{@Beanpublic WebFilter corsFilter() {return (ServerWebExchange ctx, WebFilterChain chain) -> {ServerHttpRequest request = ctx.getRequest();if (CorsUtils.isCorsRequest(request)) {HttpHeaders requestHeaders = request.getHeaders();ServerHttpResponse response = ctx.getResponse();HttpMethod requestMethod = requestHeaders.getAccessControlRequestMethod();HttpHeaders headers = response.getHeaders();headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, requestHeaders.getOrigin());headers.addAll(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS,requestHeaders.getAccessControlRequestHeaders());if (requestMethod != null) {headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, requestMethod.name());}headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");headers.add(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, "*");if (request.getMethod() == HttpMethod.OPTIONS) {response.setStatusCode(HttpStatus.OK);return Mono.empty();}}return chain.filter(ctx);};}
}

http://www.ppmy.cn/news/7281.html

相关文章

加速度计和陀螺仪模型(imu元件)分析

** 一、先分析加速度 ** 1、3自由度&#xff1a;3个轴方向的加速度/力的模型很好理解&#xff0c;前后X&#xff0c;左右Y&#xff0c;上下Z&#xff1b; 2、3自由度: 沿前后轴X方向的滚动&#xff0c;左右轴Y方向的俯仰&#xff0c;上下轴Z方向的偏航&#xff1b; 这三个自由…

RabbitMQ 订阅模型-路由模式

订阅模型-路由模式&#xff0c;此时生产者发送消息时需要指定 RoutingKey&#xff0c;即路由 Key&#xff0c;Exchange 接收到消息时转发到与 RoutingKey 相匹配的队列中。 在 Direct 模型下&#xff1a; 队列与交换机绑定&#xff0c;不能任意绑定&#xff0c;而要指定一个 Ro…

RocketMQ原理篇

文章目录broker与NameServerMessageQueue与Topic的关系生产者、消费者写入读取 消息CommitLog生产者消费者组broker与NameServer 基于 Dledger 实现 RocketMQ 高可用自动切换 broker 会每隔 30 秒向 NameServer 发送一个的心跳 &#xff0c;NameServer 收到一个心跳 会更新对…

元素偏移量 offset、元素可视区 client和元素滚动 scroll

1、元素偏移量 offset 系列 1.1、offset 概述 offset 翻译过来就是偏移量&#xff0c; 我们使用 offset系列相关属性可以动态的得到该元素的位置&#xff08;偏移&#xff09;、大小等。 获得元素距离带有定位父元素的位置获得元素自身的大小&#xff08;宽度高度&#xff09…

SegeX MemDC:实用型双缓冲内存DC (内存DC 封装MemDC)(附免费源代码)

----哆啦刘小洋 原创&#xff0c;转载需说明出处 2022-12-28 SegeX MemDC1 简介2 基础双缓存技术2.1 MFC绘图机制2.1.1 Window绘图消息2.1.2 背景刷新与屏幕闪烁2.2 双缓存技术消除屏幕闪烁2.3 封装3 更加实用的扩充1 简介 在VC中用MFC绘制图像时&#xff0c;为避免屏幕闪烁&a…

【Qt】QtCreator远程部署、调试程序

1、添加远程设备 1)QtCreator 工具–> 选项 --> 设备 --> 添加 2)设备设置向导选择–> Generic Linux Device --> 开启向导 3)填写“标识配置的名称”(随便写)、设备IP、用户名 --> 下一步 4)选择配对秘密文件,第一次配对,可以不填写,点击“下一…

Shell程序编写猜数字的小游戏

文章目录 目录 文章目录 前言 一、设计思路 二、代码编写 三、效果图 总结 前言 在学习Linux课程中学习了一点简单的shell语法&#xff0c;实现了一个猜数字功能的程序。感兴趣的可以看完后自己手动编写玩玩~这个小游戏的编写也是把基础的shell语法基本上都用到了&#…

学习HTTP协议,这一篇就够啦 ~~

HTTP协议一、什么是HTTP1.1 应用层协议1.2 HTTP1.3 HTTP协议的工作过程二、HTTP协议格式2.1 Fiddler抓包工具2.2 协议格式三、HTTP请求 (Request)3.1 认识 "方法" (method)3.1.1 GET 方法3.1.2 POST 方法3.1.3 GET和POST比较3.1.4 其他方法3.2 认识URL3.2.1 URL基本格…