
Soul Gateway Learning Redirect Plugin
Introduction
When the Soul gateway makes a proxy call to the target service, it can use redirect the plug-in to redirect the request. There are two scenarios: one is to redirectUrl configure it as a third-party URL address and directly use 308 it to forward and jump, and the other is redirectUrl to forward the configuration beginning with / to the gateway itself.
Plug-in configuration
- In
soul-admin– > Plug-in Management – >redirect, set to on. - Dependencies added
redirectmaveninsoul-bootstrapthe project'spom.xmlfiles. - Set the selector rule in the
soul- adminbackground. Only the matching request will be forwarded and redirected. Please see for details: Selector and Rule Configuration.
Maven dependency
Add the plug-in dependency in the soul-bootstrap project pom.xml file.
<dependency>
<groupId>org.dromara</groupId>
<artifactId>soul-spring-boot-starter-plugin-redirect</artifactId>
<version>${last.version}</version>
</dependency>Scenes
As the name implies,
redirecta plug-in isuria redirection and redirection of.
Redirect
- When we
Ruleconfigure the custom path, it should be a reachable service path. - When the request is matched, the service jump will be performed
308according to the user-defined pathSoul gateway.

Gateway self-interface forwarding
- When the matching rules are met, the service internally forwards using
DispatcherHandlerthe internal interface. - To realize the gateway's own interface forwarding, we need to start with the prefix in the configuration path
/. The specific configuration is shown in the following figure.

Source Code Parsing
Before parsing redirect the redirect source code, it's important to understand that the Soul Gateway is based on the SpringBoot WebFlux implementation, where WebFlux requests are handled by default DispatcherHandler if nothing is configured by default. This is the responsive MVC processing core. Take a look at the initialization:
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 related
this.handlerMappings = Collections.unmodifiableList(mappings);
Map<String, HandlerAdapter> adapterBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
// handlerAdapter related
this.handlerAdapters = new ArrayList(adapterBeans.values());
AnnotationAwareOrderComparator.sort(this.handlerAdapters);
Map<String, HandlerResultHandler> beans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerResultHandler.class, true, false);
// resultHandler related
this.resultHandlers = new ArrayList(beans.values());
AnnotationAwareOrderComparator.sort(this.resultHandlers);
}After that, we are familiar with the MVC core processing DispatcherHandler#handle method.
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);
});
}To figure out how to handle it by default DispatcherHandler, let's talk about Soul Gateway, SoulWebHandler which implements the WebHandler interface. And then BeanName replace that previously DispatcherHandler registered default proces handler with the declaration webHandler.
@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);
}So far, we understand that the default request has been SoulWebHandler#handle processed. What if we need to forward it to the gateway itself MVC? The following is DispatcherHandler injected during initialization RedirectPlugin, and then DispatcherHandler distributed according to the specific request. The specific core code is as follows:
@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);
}
// Handle self-forwarding paths starting with "/"
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 {
// Perform a 308 redirect
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.PERMANENT_REDIRECT);
response.getHeaders().add(HttpHeaders.LOCATION, redirectHandle.getRedirectURI());
return response.setComplete();
}
}