1.运行 context-path 插件

我们在使用 soul 网关时,yml 文件里就配置了 contextPath,不过我们可以使用 context_path 插件进行重写,这也体现了 soul 网关的灵活配置

登录 soul-admin 开启 context-path 插件,选择器规则配置如下

我们使用网关 http://localhost:9195/context/http/order/findById?id=3 访问时,真正执行的 url 就是 http://192.168.1.99:8188/order/findById?id=3

接下来看看 ContextPathMappingPlugin 是怎么执行的。

    protected Mono<Void> doExecute(final ServerWebExchange exchange, final SoulPluginChain chain, final SelectorData selector, final RuleData rule) {
        final SoulContext soulContext = exchange.getAttribute(Constants.CONTEXT);
        assert soulContext != null;
        final String handle = rule.getHandle();
        //获取配置的 context path
        final ContextMappingHandle contextMappingHandle = GsonUtils.getInstance().fromJson(handle, ContextMappingHandle.class);
        if (Objects.isNull(contextMappingHandle) || StringUtils.isBlank(contextMappingHandle.getContextPath())) {
            log.error("context path mapping rule configuration is null :{}", rule);
            return chain.execute(exchange);
        }
        //判断请求的路径是不是以我们配置的 context path 开头
        if (!soulContext.getPath().startsWith(contextMappingHandle.getContextPath())) {
            Object error = SoulResultWrap.error(SoulResultEnum.CONTEXT_PATH_ERROR.getCode(), SoulResultEnum.CONTEXT_PATH_ERROR.getMsg(), null);
            return WebFluxResultUtils.result(exchange, error);
        }
        //使用 context path 获取请求的url
        this.buildContextPath(soulContext, contextMappingHandle);
        return chain.execute(exchange);
    }

    private void buildContextPath(final SoulContext context, final ContextMappingHandle handle) {
        context.setContextPath(handle.getContextPath());
        if (!StringUtils.isBlank(handle.getRealUrl())) {
            log.info("context path mappingPlugin replaced old :{} , real:{}", context.getRealUrl(), handle.getRealUrl());
            context.setRealUrl(handle.getRealUrl());
            return;
        }
        //获取真实的 url
        Optional<String> optional = Arrays.stream(context.getPath()
                .split(handle.getContextPath()))
                .reduce((first, last) -> last);
        optional.ifPresent(context::setRealUrl);
    }

2.运行 rewrite 插件,规则配置如下

这里就重写了我们的 url,真正的访问地址就被修改了。

看下 rewrite 插件的源码,这部分比较简单,就是拿到 rewrite_uri,放到 exchange 里。接着就继续执行下一个插件。

    @Override
    protected Mono<Void> doExecute(final ServerWebExchange exchange, final SoulPluginChain chain, final SelectorData selector, final RuleData rule) {
        String handle = rule.getHandle();
        final RewriteHandle rewriteHandle = GsonUtils.getInstance().fromJson(handle, RewriteHandle.class);
        if (Objects.isNull(rewriteHandle) || StringUtils.isBlank(rewriteHandle.getRewriteURI())) {
            log.error("uri rewrite rule can not configuration:{}", handle);
            return chain.execute(exchange);
        }
        //放到 exchange
        exchange.getAttributes().put(Constants.REWRITE_URI, rewriteHandle.getRewriteURI());
        return chain.execute(exchange);
    }

使用是在 DividePlugin 使用的

    private String buildRealURL(final String domain, final SoulContext soulContext, final ServerWebExchange exchange) {
        String path = domain;
        //获取 rewrite 
        final String rewriteURI = (String) exchange.getAttributes().get(Constants.REWRITE_URI);
        if (StringUtils.isNoneBlank(rewriteURI)) {
            path = path + rewriteURI;
        } else {
            final String realUrl = soulContext.getRealUrl();
            if (StringUtils.isNoneBlank(realUrl)) {
                path = path + realUrl;
            }
        }
        String query = exchange.getRequest().getURI().getQuery();
        if (StringUtils.isNoneBlank(query)) {
            return path + "?" + query;
        }
        return path;
    }

这两部分的源码实现都比较简单。但却为我们使用 soul 网关时,多提供了一些选择。