一、路由规则
Spring Cloud Gateway 的功能很强大,前面我们只是使用了 predicates 进行了简单的条件匹配,其实Spring Cloud Gataway 帮我们内置了很多 Predicates 功能。在 Spring Cloud Gateway 中 Spring 利用Predicate 的特性实现了各种路由匹配规则,有通过 Header、请求参数等不同的条件来进行作为条件匹配到对应的路由。

#路由断言之后匹配 spring: cloud: gateway: routes: - id: after_route uri: https://xxxx.com #路由断言之前匹配 predicates: - After=xxxxx #路由断言之前匹配 spring: cloud: gateway: routes: - id: before_route uri: https://xxxxxx.com predicates: - Before=xxxxxxx #路由断言之间 spring: cloud: gateway: routes: - id: between_route uri: https://xxxx.com predicates: - Between=xxxx,xxxx #路由断言Cookie匹配,此predicate匹配给定名称(chocolate)和正则表达式(ch.p) spring: cloud: gateway: routes: - id: cookie_route uri: https://xxxx.com predicates: - Cookie=chocolate, ch.p #路由断言Header匹配,header名称匹配X-Request-Id,且正则表达式匹配\d+ spring: cloud: gateway: routes: - id: header_route uri: https://xxxx.com predicates: - Header=X-Request-Id, \d+ #路由断言匹配Host匹配,匹配下面Host主机列表,**代表可变参数 spring: cloud: gateway: routes: - id: host_route uri: https://xxxx.com predicates: - Host=**.somehost.org,**.anotherhost.org #路由断言Method匹配,匹配的是请求的HTTP方法 spring: cloud: gateway: routes: - id: method_route uri: https://xxxx.com predicates: - Method=GET #路由断言匹配,{segment}为可变参数 spring: cloud: gateway: routes: - id: host_route uri: https://xxxx.com predicates: - Path=/foo/{segment},/bar/{segment} #路由断言Query匹配,将请求的参数param(baz)进行匹配,也可以进行regexp正则表达式匹配 (参数包含foo,并且foo的值匹配ba.) spring: cloud: gateway: routes: - id: query_route uri: https://xxxx.com predicates: - Query=baz 或 Query=foo,ba. #路由断言RemoteAddr匹配,将匹配192.168.1.1~192.168.1.254之间的ip地址,其中24为子网掩码位数即255.255.255.0 spring: cloud: gateway: routes: - id: remoteaddr_route uri: https://example.org predicates: - RemoteAddr=192.168.1.1/24
二、动态路由
和zuul网关类似,在SpringCloud GateWay中也支持动态路由:即自动的从注册中心中获取服务列表并访问。
1、添加注册中心依赖
2、配置动态路由:修改application.yml 配置文件,修改访问映射的URL为服务名称
server: port: 8080 #服务端口 spring: application: name: api-gateway #指定服务名 cloud: gateway: routes: - id: product-service uri: lb://shop-service-product predicates: - Path=/product/**
- uri : uri以lb: //开头(lb代表从注册中心获取服务),后面接的就是你需要转发到的服务名称
三、重写转发路径
在SpringCloud Gateway中,路由转发是直接将匹配的路由path直接拼接到映射路径(URI)之后,那么在微服务开发中往往没有那么便利。这里就可以通过RewritePath机制来进行路径重写。
例:修改application.yml ,将匹配路径改为/product-service/**
server: port: 8080 #服务端口 spring: application: name: api-gateway #指定服务名 cloud: gateway: routes: - id: product-service uri: lb://shop-service-product predicates: - Path=/product-service/**
重新启动网关,我们在浏览器访问http://127.0.0.1:8080/product-service/product/1,会抛出404。这是由于路由转发规则默认转发到商品微服务( http://127.0.0.1:9002/productservice/product/1 )路径上,而商品微服务又没有product-service 对应的映射配置。
添加RewritePath重写转发路径
修改application.yml,添加重写规则。
server: port: 8080 #服务端口 spring: application: name: api-gateway #指定服务名 cloud: gateway: routes: - id: product-service uri: lb://shop-service-product predicates: - Path=/product-service/** filters: - RewritePath=/product-service/(?<segment>.*), /$\{segment}
通过RewritePath配置重写转发的url,将/product-service/(?.*),重写为{segment},然后转发到订单微服务。比如在网页上请求http://localhost:8080/product-service/product/1,此时会将请求转发到http://127.0.0.1:9002/product/1( 值得注意的是在yml文档中 $ 要写成 $\ )
四、自动根据微服务名称转发
配置如下:
server: port: 8080 #服务端口 spring: application: name: api-gateway #指定服务名 cloud: gateway: #配置自动根据微服务名称进行路由转发 http://localhost:8080/shop-servie-product/product/1会自动映射到商品微服务进行转发 discovery: locator: enabled: true #开启根据服务名称自动转发 lower-case-service-id: true #微服务名称已小写形式呈现
自动路由转发的默认访问规则是
http://Gateway_HOST:Gateway_PORT/大写的serviceId/**
五、自定义谓词
步骤:
1)首先定义一个配置类,用于承载配置参数;
2)定义一个路由谓词工厂;
注:TokenRoutePredicateFactory类,RoutePredicateFactory固定,Token等于yml配置文件值
3)在配置文件中启用该路由谓词工厂;
举例:要求请求必须携带一个token,并且token值等于指定值,才能访问;
参数配置类:
public class TokenConfig { private String token; public String getToken() { return token; } public void setToken(String token) { this.token = token; } }
谓词工厂类:
@Component public class TokenRoutePredicateFactory extends AbstractRoutePredicateFactory<TokenConfig> { public TokenRoutePredicateFactory() { super(TokenConfig.class); } @Override public List<String> shortcutFieldOrder() { return Collections.singletonList("token"); } @Override public Predicate<ServerWebExchange> apply(TokenConfig tokenConfig) { // (T t) -> true return exchange -> { MultiValueMap<String, String> valueMap = exchange.getRequest().getQueryParams(); boolean flag = false; List<String> list = new ArrayList<>(); valueMap.forEach((k, v) -> { list.addAll(v); }); for (String s : list) { log.info("Token -> {} ", s); if (StringUtils.equalsIgnoreCase(s, tokenConfig.getToken())) { flag = true; break; } } return flag; }; } }
yml配置:
spring: cloud: #配置SpringCloudGateway的路由 gateway: discovery: locator: enabled: true #开启根据服务名称自动转发 lower-case-service-id: true #微服务名称已小写形式呈现 routes: - id: order-service uri: lb://service-order predicates: - Token=123456 - Path=/order/**
访问:http://localhost:8080/order/buy/1?token=1234报错404,访问:http://localhost:8080/order/buy/1?token=123456,正确
六、自定义谓词不匹配页面
处理的顶层接口是WebExceptionHandler,默认实现是DefaultErrorWebExceptionHandler,我们需要覆盖它的默认实现DefaultErrorWebExceptionHandler,覆盖里面的方法,在方法里面编写我们想要返回的结果
public class MyErrorWebExceptionHandler extends DefaultErrorWebExceptionHandler { public MyErrorWebExceptionHandler(ErrorAttributes errorAttributes, ResourceProperties resourceProperties, ErrorProperties errorProperties, ApplicationContext applicationContext) { super(errorAttributes, resourceProperties, errorProperties, applicationContext); } /** * 获取异常属性 */ @Override protected Map<String, Object> getErrorAttributes(ServerRequest request, boolean includeStackTrace) { int code = 500; Throwable error = super.getError(request); if (error instanceof org.springframework.cloud.gateway.support.NotFoundException) { code = 404; } return response(code, this.buildMessage(request, error)); } /** * 指定响应处理方法为JSON处理的方法 * * @param errorAttributes */ @Override protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) { return RouterFunctions.route(RequestPredicates.all(), this::renderErrorResponse); } /** * 根据code获取对应的HttpStatus * @param errorAttributes */ @Override protected HttpStatus getHttpStatus(Map<String, Object> errorAttributes) { int statusCode = (int) errorAttributes.get("code"); return HttpStatus.valueOf(statusCode); } /** * 构建异常信息 * @param request * @param ex * @return */ private String buildMessage(ServerRequest request, Throwable ex) { StringBuilder message = new StringBuilder("Failed to handle request ["); message.append(request.methodName()); message.append(" "); message.append(request.uri()); message.append("]"); if (ex != null) { message.append(": "); message.append(ex.getMessage()); } return message.toString(); } /** * 构建返回的JSON数据格式 * * @param status 状态码 * @param errorMessage 异常信息 * @return */ public static Map<String, Object> response(int status, String errorMessage) { Map<String, Object> map = new HashMap<>(); map.put("code", status); map.put("message", errorMessage); map.put("data", null); return map; } }
配置注册类
@Configuration public class GatewayConfiguration { private final ServerProperties serverProperties; private final ApplicationContext applicationContext; private final ResourceProperties resourceProperties; private final List<ViewResolver> viewResolvers; private final ServerCodecConfigurer serverCodecConfigurer; public GatewayConfiguration(ServerProperties serverProperties, ApplicationContext applicationContext, ResourceProperties resourceProperties, ObjectProvider<List<ViewResolver>> viewResolversProvider, ServerCodecConfigurer serverCodecConfigurer) { this.serverProperties = serverProperties; this.applicationContext = applicationContext; this.resourceProperties = resourceProperties; this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList); this.serverCodecConfigurer = serverCodecConfigurer; } @Bean("myErrorWebExceptionHandler") @Order(Ordered.HIGHEST_PRECEDENCE) public ErrorWebExceptionHandler myErrorWebExceptionHandler(ErrorAttributes errorAttributes) { MyErrorWebExceptionHandler exceptionHandler = new MyErrorWebExceptionHandler( errorAttributes, this.resourceProperties, this.serverProperties.getError(), this.applicationContext); exceptionHandler.setViewResolvers(this.viewResolvers); exceptionHandler.setMessageWriters(this.serverCodecConfigurer.getWriters()); exceptionHandler.setMessageReaders(this.serverCodecConfigurer.getReaders()); return exceptionHandler; } }
返回提示:
