Soul
網關在對目標服務進行代理調用的時候,能夠使用 redirect
插件來重定向請求。其中包含兩種場景:一種把 redirectUrl
配置爲第三方URL 地址,直接使用 308
進行轉發跳轉,另外一種是把 redirectUrl
配置以 /
開頭的轉發到網關自身。web
soul-admin
–> 插件管理 –> redirect
,設置爲開啓。soul-bootstrap
項目的 pom.xml
文件中添加 redirect
的 maven
依賴。soul- admin
後臺設置選擇器規則,只有匹配的請求,纔會進行轉發和重定向,請詳細看:選擇器規則。在 soul-bootstrap
工程的 pom.xml
文件中添加插件依賴。spring
<dependency>
<groupId>org.dromara</groupId>
<artifactId>soul-spring-boot-starter-plugin-redirect</artifactId>
<version>${last.version}</version>
</dependency>
複製代碼
顧名思義,
redirect
插件就是對uri
的從新轉發和重定向。bootstrap
Rule
配置自定義路徑時,應該爲一個可達的服務路徑。Soul 網關
會進行 308
服務跳轉。DispatcherHandler
內部接口轉發。/
做爲前綴開始,具體配置以下圖。在解析 redirect
重定向源碼以前,有必要說一些大前提,咱們明白 Soul 網關基於 SpringBoot WebFlux 實現,其中對於 WebFlux
若是默認什麼都不配置,請求會默認執行 DispatcherHandler
處理,這個是響應式 MVC
的處理核心,能夠看一下初始化:markdown
protected void initStrategies(ApplicationContext context) {
Map<String, HandlerMapping> mappingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
ArrayList<HandlerMapping> mappings = new ArrayList(mappingBeans.values());
AnnotationAwareOrderComparator.sort(mappings);
// handlerMapping 相關
this.handlerMappings = Collections.unmodifiableList(mappings);
Map<String, HandlerAdapter> adapterBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
// handlerAdapter 相關
this.handlerAdapters = new ArrayList(adapterBeans.values());
AnnotationAwareOrderComparator.sort(this.handlerAdapters);
Map<String, HandlerResultHandler> beans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerResultHandler.class, true, false);
// resultHandler 相關
this.resultHandlers = new ArrayList(beans.values());
AnnotationAwareOrderComparator.sort(this.resultHandlers);
}
複製代碼
再以後就是咱們熟悉的 MVC
核心處理 DispatcherHandler#handle
方法app
public Mono<Void> handle(ServerWebExchange exchange) {
return this.handlerMappings == null ? this.createNotFoundError() : Flux.fromIterable(this.handlerMappings).concatMap((mapping) -> {
return mapping.getHandler(exchange);
}).next().switchIfEmpty(this.createNotFoundError()).flatMap((handler) -> {
return this.invokeHandler(exchange, handler);
}).flatMap((result) -> {
return this.handleResult(exchange, result);
});
}
複製代碼
搞清楚默認 DispatcherHandler
如何處理,咱們再來講一下 Soul 網關,SoulWebHandler
實現了 WebHandler
接口,再把 BeanName
聲明爲 webHandler
替代了以前 DispatcherHandler
註冊成默認處理 handler
。maven
@Bean("webHandler")
public SoulWebHandler soulWebHandler(final ObjectProvider<List<SoulPlugin>> plugins) {
List<SoulPlugin> pluginList = plugins.getIfAvailable(Collections::emptyList);
List<SoulPlugin> soulPlugins = pluginList.stream()
.sorted(Comparator.comparingInt(SoulPlugin::getOrder)).collect(Collectors.toList());
soulPlugins.forEach(soulPlugin -> log.info("load plugin:[{}] [{}]", soulPlugin.named(), soulPlugin.getClass().getName()));
return new SoulWebHandler(soulPlugins);
}
複製代碼
到此爲止咱們明白了,默認請求都經過了 SoulWebHandler#handle
處理,若是咱們須要轉發到網關自身的 MVC
如何作呢?下面經過初始化RedirectPlugin
的時候把 DispatcherHandler
注入,根據具體請求再由 DispatcherHandler
分發,具體核心代碼以下:ide
@Override
protected Mono<Void> doExecute(final ServerWebExchange exchange, final SoulPluginChain chain,
final SelectorData selector, final RuleData rule) {
final String handle = rule.getHandle();
final RedirectHandle redirectHandle = GsonUtils.getInstance().fromJson(handle, RedirectHandle.class);
if (Objects.isNull(redirectHandle) || StringUtils.isBlank(redirectHandle.getRedirectURI())) {
log.error("uri redirect rule can not configuration: {}", handle);
return chain.execute(exchange);
}
// 處理以 / 開頭自身轉發
if (redirectHandle.getRedirectURI().startsWith(ROOT_PATH_PREFIX)) {
ServerHttpRequest request = exchange.getRequest().mutate()
.uri(Objects.requireNonNull(UriUtils.createUri(redirectHandle.getRedirectURI()))).build();
ServerWebExchange mutated = exchange.mutate().request(request).build();
return dispatcherHandler.handle(mutated);
} else {
// 不然就 308 跳轉
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.PERMANENT_REDIRECT);
response.getHeaders().add(HttpHeaders.LOCATION, redirectHandle.getRedirectURI());
return response.setComplete();
}
}
複製代碼
本文使用 文章同步助手 同步spring-boot