Spring Cloud GateWay源码解析实战 - RoutePredicateHandlerMapping
2023-09-27 14:19:47 时间
package org.springframework.cloud.gateway.handler;
import java.util.function.Function;
import reactor.core.publisher.Mono;
import org.springframework.cloud.gateway.config.GlobalCorsProperties;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.core.env.Environment;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.reactive.handler.AbstractHandlerMapping;
import org.springframework.web.server.ServerWebExchange;
import static org.springframework.cloud.gateway.handler.RoutePredicateHandlerMapping.ManagementPortType.DIFFERENT;
import static org.springframework.cloud.gateway.handler.RoutePredicateHandlerMapping.ManagementPortType.DISABLED;
import static org.springframework.cloud.gateway.handler.RoutePredicateHandlerMapping.ManagementPortType.SAME;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_HANDLER_MAPPER_ATTR;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_PREDICATE_ROUTE_ATTR;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR;
/**
* @author Spencer Gibb
*/
public class RoutePredicateHandlerMapping extends AbstractHandlerMapping {
private final FilteringWebHandler webHandler;
private final RouteLocator routeLocator;
private final Integer managementPort;
private final ManagementPortType managementPortType;
public RoutePredicateHandlerMapping(FilteringWebHandler webHandler,
RouteLocator routeLocator, GlobalCorsProperties globalCorsProperties,
Environment environment) {
this.webHandler = webHandler;
this.routeLocator = routeLocator;
this.managementPort = getPortProperty(environment, "management.server.");
this.managementPortType = getManagementPortType(environment);
setOrder(1);
setCorsConfigurations(globalCorsProperties.getCorsConfigurations());
}
private ManagementPortType getManagementPortType(Environment environment) {
Integer serverPort = getPortProperty(environment, "server.");
if (this.managementPort != null && this.managementPort < 0) {
return DISABLED;
}
return ((this.managementPort == null
|| (serverPort == null && this.managementPort.equals(8080))
|| (this.managementPort != 0 && this.managementPort.equals(serverPort)))
? SAME : DIFFERENT);
}
private static Integer getPortProperty(Environment environment, String prefix) {
return environment.getProperty(prefix + "port", Integer.class);
}
@Override
protected Mono<?> getHandlerInternal(ServerWebExchange exchange) {
// don't handle requests on management port if set and different than server port
if (this.managementPortType == DIFFERENT && this.managementPort != null
&& exchange.getRequest().getURI().getPort() == this.managementPort) {
return Mono.empty();
}
exchange.getAttributes().put(GATEWAY_HANDLER_MAPPER_ATTR, getSimpleName());
return lookupRoute(exchange)
// .log("route-predicate-handler-mapping", Level.FINER) //name this
.flatMap((Function<Route, Mono<?>>) r -> {
exchange.getAttributes().remove(GATEWAY_PREDICATE_ROUTE_ATTR);
if (logger.isDebugEnabled()) {
logger.debug(
"Mapping [" + getExchangeDesc(exchange) + "] to " + r);
}
exchange.getAttributes().put(GATEWAY_ROUTE_ATTR, r);
return Mono.just(webHandler);
}).switchIfEmpty(Mono.empty().then(Mono.fromRunnable(() -> {
exchange.getAttributes().remove(GATEWAY_PREDICATE_ROUTE_ATTR);
if (logger.isTraceEnabled()) {
logger.trace("No RouteDefinition found for ["
+ getExchangeDesc(exchange) + "]");
}
})));
}
@Override
protected CorsConfiguration getCorsConfiguration(Object handler,
ServerWebExchange exchange) {
// TODO: support cors configuration via properties on a route see gh-229
// see RequestMappingHandlerMapping.initCorsConfiguration()
// also see
// https://github.com/spring-projects/spring-framework/blob/master/spring-web/src/test/java/org/springframework/web/cors/reactive/CorsWebFilterTests.java
return super.getCorsConfiguration(handler, exchange);
}
// TODO: get desc from factory?
private String getExchangeDesc(ServerWebExchange exchange) {
StringBuilder out = new StringBuilder();
out.append("Exchange: ");
out.append(exchange.getRequest().getMethod());
out.append(" ");
out.append(exchange.getRequest().getURI());
return out.toString();
}
protected Mono<Route> lookupRoute(ServerWebExchange exchange) {
return this.routeLocator.getRoutes()
// individually filter routes so that filterWhen error delaying is not a
// problem
.concatMap(route -> Mono.just(route).filterWhen(r -> {
// add the current route we are testing
exchange.getAttributes().put(GATEWAY_PREDICATE_ROUTE_ATTR, r.getId());
return r.getPredicate().apply(exchange);
})
// instead of immediately stopping main flux due to error, log and
// swallow it
.doOnError(e -> logger.error(
"Error applying predicate for route: " + route.getId(),
e))
.onErrorResume(e -> Mono.empty()))
// .defaultIfEmpty() put a static Route not found
// or .switchIfEmpty()
// .switchIfEmpty(Mono.<Route>empty().log("noroute"))
.next()
// TODO: error handling
.map(route -> {
if (logger.isDebugEnabled()) {
logger.debug("Route matched: " + route.getId());
}
validateRoute(route, exchange);
return route;
});
/*
* TODO: trace logging if (logger.isTraceEnabled()) {
* logger.trace("RouteDefinition did not match: " + routeDefinition.getId()); }
*/
}
/**
* Validate the given handler against the current request.
* <p>
* The default implementation is empty. Can be overridden in subclasses, for example
* to enforce specific preconditions expressed in URL mappings.
* @param route the Route object to validate
* @param exchange current exchange
* @throws Exception if validation failed
*/
@SuppressWarnings("UnusedParameters")
protected void validateRoute(Route route, ServerWebExchange exchange) {
}
protected String getSimpleName() {
return "RoutePredicateHandlerMapping";
}
public enum ManagementPortType {
/**
* The management port has been disabled.
*/
DISABLED,
/**
* The management port is the same as the server port.
*/
SAME,
/**
* The management port and server port are different.
*/
DIFFERENT;
}
}
相关文章
- 微服务下前后端分离的统一认证授权服务,基于Spring Security OAuth2 + Spring Cloud Gateway实现单点登录
- 关于使用Spring Cloud Gateway网关网络连接的4个重要的参数配置
- Spring Cloud Gateway 使用 Kubernetes 作为服务发现
- Too many open files ( spring-cloud-gateway )
- Spring MVC found on classpath, which is incompatible with Spring Cloud Gateway at this time
- 拜托!面试请不要再问我Spring Cloud底层原理
- Spring Cloud使用Zuul实现容错回退功能
- Spring Cloud Gateway现高风险漏洞,建议采取措施加强防护
- Spring Cloud Stream消费失败后的处理策略(一):自动重试
- Spring Cloud Feign的文件上传实现
- Spring Cloud Gateway - 快速开始
- 服务注册发现、配置中心集一体的 Spring Cloud Consul
- Spring Cloud Gateway 请求报文获取 高性能实现方法
- Spring Cloud Zuul的一个坑
- spring cloud 加入配置中心后的 部分 配置文件优先级
- Spring Cloud Gateway简单使用
- Spring Cloud Gateway 整合 sentinel 实现流控熔断
- Spring Cloud Gateway 整合 knife4j 聚合接口文档
- Spring Cloud Alibaba 服务网关 Gateway(十)
- Spring Cloud : Gateway 整合Swagger (八)
- Spring Cloud : Gateway Redis动态路由 (七)
- Spring Cloud :Gateway 路由定义定位器 RouteDefinitionLocator (三)
- Spring Cloud Alibaba 微服务组件 Nacos 注册中心(三)
- Spring AOP实现原理
- Spring Cloud Config实现集群配置中心
- Spring cloud zuul跨域(二)
- Spring cloud zuul跨域(一)
- spring security源码分析心得
- spring mvc DispatcherServlet详解之拾忆工具类utils