超越源码阅读之shenyu网关-响应参数插件

超越源码阅读之shenyu网关-响应参数插件

我们大家绝大数人碰到源码一开始都是比较无从下手的,不知道从哪开始阅读、阅读了有什么用。我认为有一种方法,可以解决上面的问题,让我们既知道了从哪开始阅读源码,又能够从阅读源码中获得收获。
那就是通过阅读某一次开源commit 、某一次ISSUE的提出,从这个入口出发,去探索源码。
通过上面的方法,我们可以从堆砌的源码中脱离开来,去阅读其中让我们更有兴趣的部分,都说兴趣是最好的老师,兴趣老师会引导我们走向正确的道路,让我们获取我们需要的收获,最后甚至还会送给你一朵小红花当作奖励嘻嘻🤭。

前瞻

那就让我们从这次开源提交开始:ISSUE链接

从这次ISSUE 可以知道,本次开源commit是增加一个接口响应码的插件

探索

通过对所有提交的查看,把核心类锁定在ModifyResponsePlugin ,以下就核心类开始对本次commit进行探索。

我们可以看到核心方法doExecute 把shenyu对响应插件的自定义规则Rule ,传入到Spring框架自带的ServerWebExchange 进行修改,入参new ModifyResponseDecorator(exchange, ruleHandle)).build()也就是提交者对响应参数的所作出的修改。

ModifyResponseDecorator类如下,buildModifiedResponse方法就是关键,修改的代码没什么好说的可以自己看看。

java 复制代码
static class ModifyResponseDecorator extends ServerHttpResponseDecorator {

        private final ServerWebExchange exchange;

        private final ModifyResponseRuleHandle ruleHandle;

        ModifyResponseDecorator(final ServerWebExchange exchange,
                                final ModifyResponseRuleHandle ruleHandle) {
            super(exchange.getResponse());
            this.exchange = exchange;
            this.ruleHandle = ruleHandle;
        }

        @Override
        @NonNull
        public Mono<Void> writeWith(@NonNull final Publisher<? extends DataBuffer> body) {
            final Mono<DataBuffer> dataBufferMono = DataBufferUtils.join(body);
            buildModifiedResponse(body);
            return dataBufferMono.flatMap(dataBuffer -> {
                byte[] bytes = new byte[dataBuffer.readableByteCount()];
                dataBuffer.read(bytes);
                return WebFluxResultUtils.result(this.exchange, modifyBody(bytes));
            });
        }

        private void buildModifiedResponse(final Publisher<? extends DataBuffer> body) {
            HttpHeaders httpHeaders = new HttpHeaders();
            // add origin headers
            httpHeaders.addAll(this.getHeaders());

            // add new headers
            if (MapUtils.isNotEmpty(this.ruleHandle.getAddHeaders())) {
                Map<String, String> addHeaderMap = this.ruleHandle.getAddHeaders();
                addHeaderMap.forEach(httpHeaders::add);
            }

            // set new headers
            if (MapUtils.isNotEmpty(this.ruleHandle.getSetHeaders())) {
                Map<String, String> setHeaderMap = this.ruleHandle.getSetHeaders();
                setHeaderMap.forEach(httpHeaders::set);
            }

            // replace headers
            if (MapUtils.isNotEmpty(this.ruleHandle.getReplaceHeaderKeys())) {
                Map<String, String> replaceHeaderMap = this.ruleHandle.getReplaceHeaderKeys();
                replaceHeaderMap.forEach((key, value) -> httpHeaders.replace(key, Collections.singletonList(value)));
            }

            // remove headers
            if (CollectionUtils.isNotEmpty(this.ruleHandle.getRemoveHeaderKeys())) {
                Set<String> removeHeaderList = this.ruleHandle.getRemoveHeaderKeys();
                removeHeaderList.forEach(httpHeaders::remove);
            }

            // reset http status
            if (this.ruleHandle.getStatusCode() > 0) {
                this.setStatusCode(HttpStatus.valueOf(this.ruleHandle.getStatusCode()));
            }

            // reset http headers
            this.getDelegate().getHeaders().clear();
            this.getDelegate().getHeaders().putAll(httpHeaders);
        }

        private byte[] modifyBody(final byte[] responseBody) {
            try {
                String bodyStr = modifyBody(new String(responseBody, StandardCharsets.UTF_8));
                LOG.info("the body string {}", bodyStr);
                return bodyStr.getBytes(StandardCharsets.UTF_8);
            } catch (Exception e) {
                LOG.error("modify response error", e);
                throw new ShenyuException(String.format("response modify failure. %s", e.getLocalizedMessage()));
            }
        }

        private String modifyBody(final String jsonValue) {
            DocumentContext context = JsonPath.parse(jsonValue);
            if (CollectionUtils.isNotEmpty(this.ruleHandle.getAddBodyKeys())) {
                this.ruleHandle.getAddBodyKeys().forEach(info -> context.put(info.getPath(), info.getKey(), info.getValue()));
            }
            if (CollectionUtils.isNotEmpty(this.ruleHandle.getReplaceBodyKeys())) {
                this.ruleHandle.getReplaceBodyKeys().forEach(info -> context.renameKey(info.getPath(), info.getKey(), info.getValue()));
            }
            if (CollectionUtils.isNotEmpty(this.ruleHandle.getRemoveBodyKeys())) {
                this.ruleHandle.getRemoveBodyKeys().forEach(context::delete);
            }
            return context.jsonString();
        }
    }

总结

仔细一看buildModifiedResponse 方法的入参body的没有使用到,且该方法的命名也有不妥,modifyResponseHeadersAndStatus这个命名更易理解也更符合代码修改。赶紧提一次commit哈哈:commit链接

又收获了一次开源提交!!!

能否感受通过commit、ISSUE这种方式来阅读源码的乐趣呢,学习了知识、获得了开源commit,何乐而不为呢

对作者感兴趣的话,不妨关注、点赞、收藏支持一下嘞!😘

相关推荐
q***71012 小时前
Spring Boot(快速上手)
java·spring boot·后端
better_liang4 小时前
每日Java面试场景题知识点之-分布式事务处理
java·微服务·面试·springcloud·分布式事务
L***d6706 小时前
Spring Boot 各种事务操作实战(自动回滚、手动回滚、部分回滚)
java·数据库·spring boot
凌波粒6 小时前
Springboot基础教程(3)--自动装配原理/静态资源处理/欢迎页
java·spring boot·后端
likuolei6 小时前
XSL-FO 软件
java·开发语言·前端·数据库
凌波粒6 小时前
SpringBoot基础教程(2)--yaml/配置文件注入/数据校验/多环境配置
java·spring boot·后端·spring
S***26756 小时前
Spring Boot环境配置
java·spring boot·后端
6***83056 小时前
什么是Spring Boot 应用开发?
java·spring boot·后端
毕设源码柳学姐6 小时前
计算机毕设 java 智慧社区服务系统 SSM 框架社区生活平台 Java 开发的便民服务与互动系统
java·开发语言·生活
U***l8326 小时前
【postgresql】分区表管理
java·数据库·postgresql