我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Spring Cloud gateway网关怎么拦截Post请求日志

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Spring Cloud gateway网关怎么拦截Post请求日志

本篇内容主要讲解“Spring Cloud gateway网关怎么拦截Post请求日志”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Spring Cloud gateway网关怎么拦截Post请求日志”吧!

gateway版本是 2.0.1

1.pom结构

(部分内部项目依赖已经隐藏)

<dependency>    <groupId>org.springframework.cloud</groupId>    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency><dependency>    <groupId>org.springframework.cloud</groupId>    <artifactId>spring-cloud-starter-gateway</artifactId></dependency><!--监控相关--><dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-actuator</artifactId></dependency><!-- redis --><!--<dependency>-->    <!--<groupId>org.springframework.boot</groupId>-->    <!--<artifactId>spring-boot-starter-data-redis</artifactId>--><!--</dependency>--><!-- test-scope --><dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-test</artifactId>    <scope>test</scope></dependency><dependency>    <groupId>ch.qos.logback</groupId>    <artifactId>logback-core</artifactId>    <version>1.1.11</version></dependency><dependency>    <groupId>ch.qos.logback</groupId>    <artifactId>logback-classic</artifactId>    <version>1.1.11</version></dependency><dependency>    <groupId>org.apache.httpcomponents</groupId>    <artifactId>httpclient</artifactId>    <version>4.5.6</version></dependency><!--第三方的jdbctemplatetool--><dependency>    <groupId>org.crazycake</groupId>    <artifactId>jdbctemplatetool</artifactId>    <version>1.0.4-RELEASE</version></dependency><dependency>    <groupId>mysql</groupId>    <artifactId>mysql-connector-java</artifactId></dependency><!-- alibaba start --><dependency>    <groupId>com.alibaba</groupId>    <artifactId>druid</artifactId></dependency>

2.表结构

CREATE TABLE `zc_log_notes` (  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '日志信息记录表主键id',  `notes` varchar(255) DEFAULT NULL COMMENT '操作记录信息',  `amenu` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '一级菜单',  `bmenu` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '二级菜单',  `ip` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '操作人ip地址,先用varchar存',  `params` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci COMMENT '请求值',  `response` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci COMMENT '返回值',  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '操作时间',  `create_user` int(11) DEFAULT NULL COMMENT '操作人id',  `end_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '响应时间',  `status` int(1) NOT NULL DEFAULT '1' COMMENT '响应结果1成功0失败',  PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=103 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='日志信息记录表';

3.实体结构

@Table(catalog = "zhiche", name = "zc_log_notes")public class LogNotes {        private Integer id;        private String notes;        private String amenu;        private String bmenu;        private String ip;        private String params;        private String response;        private Date createTime;        private Integer createUser;        private Date endTime;        private Integer status;    @Id    @GeneratedValue(strategy = GenerationType.IDENTITY)    public Integer getId() {        return id;    }    public void setId(Integer id) {        this.id = id;    }    public String getNotes() {        return notes;    }    public void setNotes(String notes) {        this.notes = notes;    }    public String getAmenu() {        return amenu;    }    public void setAmenu(String amenu) {        this.amenu = amenu;    }    public String getBmenu() {        return bmenu;    }    public void setBmenu(String bmenu) {        this.bmenu = bmenu;    }    public String getIp() {        return ip;    }    public void setIp(String ip) {        this.ip = ip;    }    public Date getCreateTime() {        return createTime;    }    public void setCreateTime(Date createTime) {        this.createTime = createTime;    }    public Integer getCreateUser() {        return createUser;    }    public void setCreateUser(Integer createUser) {        this.createUser = createUser;    }    public Date getEndTime() {        return endTime;    }    public void setEndTime(Date endTime) {        this.endTime = endTime;    }    public Integer getStatus() {        return status;    }    public void setStatus(Integer status) {        this.status = status;    }    public String getParams() {        return params;    }    public void setParams(String params) {        this.params = params;    }    public String getResponse() {        return response;    }    public void setResponse(String response) {            this.response = response;    }    public void setAppendResponse(String response){        if (StringUtils.isNoneBlank(this.response)) {            this.response = this.response + response;        } else {            this.response = response;        }    }}

4.dao层和Service层省略..

5.filter代码

1. RequestRecorderGlobalFilter 实现了GlobalFilter和Order

package com.zc.gateway.filter;import com.zc.entity.LogNotes;import com.zc.gateway.service.FilterService;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.cloud.gateway.filter.GatewayFilterChain;import org.springframework.cloud.gateway.filter.GlobalFilter;import org.springframework.core.Ordered;import org.springframework.core.io.buffer.DataBuffer;import org.springframework.core.io.buffer.DataBufferUtils;import org.springframework.http.HttpHeaders;import org.springframework.http.HttpMethod;import org.springframework.http.HttpStatus;import org.springframework.http.MediaType;import org.springframework.http.server.reactive.ServerHttpRequest;import org.springframework.http.server.reactive.ServerHttpResponse;import org.springframework.lang.Nullable;import org.springframework.stereotype.Component;import org.springframework.web.server.ServerWebExchange;import reactor.core.publisher.Flux;import reactor.core.publisher.Mono;import java.net.URI;import java.nio.CharBuffer;import java.nio.charset.Charset;import java.nio.charset.StandardCharsets;@Componentpublic class RequestRecorderGlobalFilter implements GlobalFilter, Ordered {    @Autowired    FilterService filterService;    private Logger logger = LoggerFactory.getLogger(RequestRecorderGlobalFilter.class);    @Override    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {        ServerHttpRequest originalRequest = exchange.getRequest();        URI originalRequestUrl = originalRequest.getURI();        //只记录http的请求        String scheme = originalRequestUrl.getScheme();        if ((!"http".equals(scheme) && !"https".equals(scheme))) {            return chain.filter(exchange);        }        //这是我要打印的log-StringBuilder        StringBuilder logbuilder = new StringBuilder();        //我自己的log实体        LogNotes logNotes = new LogNotes();        // 返回解码        RecorderServerHttpResponseDecorator response = new RecorderServerHttpResponseDecorator(exchange.getResponse(), logNotes, filterService);        //请求解码        RecorderServerHttpRequestDecorator recorderServerHttpRequestDecorator = new RecorderServerHttpRequestDecorator(exchange.getRequest());        //增加过滤拦截吧        ServerWebExchange ex = exchange.mutate()                .request(recorderServerHttpRequestDecorator)                .response(response)                .build();        //  观察者模式 打印一下请求log        // 这里可以在 配置文件中我进行配置//        if (logger.isDebugEnabled()) {        response.beforeCommit(() -> Mono.defer(() -> printLog(logbuilder, response)));//        }        return recorderOriginalRequest(logbuilder, ex, logNotes)                .then(chain.filter(ex))                .then();    }    private Mono<Void> recorderOriginalRequest(StringBuilder logBuffer, ServerWebExchange exchange, LogNotes logNotes) {        logBuffer.append(System.currentTimeMillis())                .append("------------");        ServerHttpRequest request = exchange.getRequest();        Mono<Void> result = recorderRequest(request, logBuffer.append("\n原始请求:\n"), logNotes);        try {            filterService.addLog(logNotes);        } catch (Exception e) {            logger.error("保存请求参数出现错误, e->{}", e.getMessage());        }        return result;    }        private Mono<Void> recorderRequest(ServerHttpRequest request, StringBuilder logBuffer, LogNotes logNotes) {        URI uri = request.getURI();        HttpMethod method = request.getMethod();        HttpHeaders headers = request.getHeaders();        logNotes.setIp(headers.getHost().getHostString());        logNotes.setAmenu("一级菜单");        logNotes.setBmenu("二级菜单");        logNotes.setNotes("操作记录");        logBuffer                .append(method.toString()).append(' ')                .append(uri.toString()).append('\n');        logBuffer.append("------------请求头------------\n");        headers.forEach((name, values) -> {            values.forEach(value -> {                logBuffer.append(name).append(":").append(value).append('\n');            });        });        Charset bodyCharset = null;        if (hasBody(method)) {            long length = headers.getContentLength();            if (length <= 0) {                logBuffer.append("------------无body------------\n");            } else {                logBuffer.append("------------body 长度:").append(length).append(" contentType:");                MediaType contentType = headers.getContentType();                if (contentType == null) {                    logBuffer.append("null,不记录body------------\n");                } else if (!shouldRecordBody(contentType)) {                    logBuffer.append(contentType.toString()).append(",不记录body------------\n");                } else {                    bodyCharset = getMediaTypeCharset(contentType);                    logBuffer.append(contentType.toString()).append("------------\n");                }            }        }        if (bodyCharset != null) {            return doRecordReqBody(logBuffer, request.getBody(), bodyCharset, logNotes)                    .then(Mono.defer(() -> {                        logBuffer.append("\n------------ end ------------\n\n");                        return Mono.empty();                    }));        } else {            logBuffer.append("------------ end ------------\n\n");            return Mono.empty();        }    }    //日志输出返回值    private Mono<Void> printLog(StringBuilder logBuilder, ServerHttpResponse response) {        HttpStatus statusCode = response.getStatusCode();        assert statusCode != null;        logBuilder.append("响应:").append(statusCode.value()).append(" ").append(statusCode.getReasonPhrase()).append('\n');        HttpHeaders headers = response.getHeaders();        logBuilder.append("------------响应头------------\n");        headers.forEach((name, values) -> {            values.forEach(value -> {                logBuilder.append(name).append(":").append(value).append('\n');            });        });        logBuilder.append("\n------------ end at ")                .append(System.currentTimeMillis())                .append("------------\n\n");        logger.info(logBuilder.toString());        return Mono.empty();    }    //    @Override    public int getOrder() {        //在GatewayFilter之前执行        return -1;    }    private boolean hasBody(HttpMethod method) {        //只记录这3种谓词的body//        if (method == HttpMethod.POST || method == HttpMethod.PUT || method == HttpMethod.PATCH)        return true;//        return false;    }    //记录简单的常见的文本类型的request的body和response的body    private boolean shouldRecordBody(MediaType contentType) {        String type = contentType.getType();        String subType = contentType.getSubtype();        if ("application".equals(type)) {            return "json".equals(subType) || "x-www-form-urlencoded".equals(subType) || "xml".equals(subType) || "atom+xml".equals(subType) || "rss+xml".equals(subType);        } else if ("text".equals(type)) {            return true;        }        //暂时不记录form        return false;    }    // 获取请求的参数    private Mono<Void> doRecordReqBody(StringBuilder logBuffer, Flux<DataBuffer> body, Charset charset, LogNotes logNotes) {        return DataBufferUtils.join(body).doOnNext(buffer -> {            CharBuffer charBuffer = charset.decode(buffer.asByteBuffer());            //记录我实体的请求体            logNotes.setParams(charBuffer.toString());            logBuffer.append(charBuffer.toString());            DataBufferUtils.release(buffer);        }).then();    }    private Charset getMediaTypeCharset(@Nullable MediaType mediaType) {        if (mediaType != null && mediaType.getCharset() != null) {            return mediaType.getCharset();        } else {            return StandardCharsets.UTF_8;        }    }}

2.RecorderServerHttpRequestDecorator 继承了ServerHttpRequestDecorator

package com.zc.gateway.filter;import com.zc.entity.LogNotes;import org.springframework.core.io.buffer.DataBuffer;import org.springframework.http.server.reactive.ServerHttpRequest;import org.springframework.http.server.reactive.ServerHttpRequestDecorator;import reactor.core.publisher.Flux;import reactor.core.publisher.Mono;import java.util.LinkedList;import java.util.List;// requestpublic class RecorderServerHttpRequestDecorator extends ServerHttpRequestDecorator {    private final List<DataBuffer> dataBuffers = new LinkedList<>();    private boolean bufferCached = false;    private Mono<Void> progress = null;    public RecorderServerHttpRequestDecorator(ServerHttpRequest delegate) {        super(delegate);    }//重写request请求体    @Override    public Flux<DataBuffer> getBody() {        synchronized (dataBuffers) {            if (bufferCached)                return copy();            if (progress == null) {                progress = cache();            }            return progress.thenMany(Flux.defer(this::copy));        }    }    private Flux<DataBuffer> copy() {        return Flux.fromIterable(dataBuffers)                .map(buf -> buf.factory().wrap(buf.asByteBuffer()));    }    private Mono<Void> cache() {        return super.getBody()                .map(dataBuffers::add)                .then(Mono.defer(()-> {                    bufferCached = true;                    progress = null;                    return Mono.empty();                }));    }}

3.RecorderServerHttpResponseDecorator 继承了 ServerHttpResponseDecorator

package com.zc.gateway.filter;import com.zc.entity.LogNotes;import com.zc.gateway.service.FilterService;import org.reactivestreams.Publisher;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.core.io.buffer.DataBufferFactory;import org.springframework.core.io.buffer.DataBufferUtils;import org.springframework.http.server.reactive.ServerHttpResponse;import org.springframework.http.server.reactive.ServerHttpResponseDecorator;import reactor.core.publisher.Flux;import reactor.core.publisher.Mono;import org.springframework.core.io.buffer.DataBuffer;import java.nio.charset.Charset;import java.util.LinkedList;import java.util.List;public class RecorderServerHttpResponseDecorator extends ServerHttpResponseDecorator {    private Logger logger = LoggerFactory.getLogger(RecorderServerHttpResponseDecorator.class);    private LogNotes logNotes;    private FilterService filterService;    RecorderServerHttpResponseDecorator(ServerHttpResponse delegate, LogNotes logNotes, FilterService filterService) {        super(delegate);        this.logNotes = logNotes;        this.filterService = filterService;    }        @Override    public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {        DataBufferFactory bufferFactory = this.bufferFactory();        if (body instanceof Flux) {            Flux<? extends DataBuffer> fluxBody = (Flux<? extends DataBuffer>) body;            Publisher<? extends DataBuffer> re = fluxBody.map(dataBuffer -> {                // probably should reuse buffers                byte[] content = new byte[dataBuffer.readableByteCount()];                // 数据读入数组                dataBuffer.read(content);                // 释放掉内存                DataBufferUtils.release(dataBuffer);                // 记录返回值                String s = new String(content, Charset.forName("UTF-8"));                logNotes.setAppendResponse(s);                try {                    filterService.updateLog(logNotes);                } catch (Exception e) {                    logger.error("Response值修改日志记录出现错误->{}", e);                }                byte[] uppedContent = new String(content, Charset.forName("UTF-8")).getBytes();                return bufferFactory.wrap(uppedContent);            });            return super.writeWith(re);        }        return super.writeWith(body);    }    @Override    public Mono<Void> writeAndFlushWith(Publisher<? extends Publisher<? extends DataBuffer>> body) {        return writeWith(Flux.from(body).flatMapSequential(p -> p));    }}
注意:

网关过滤返回值 底层用到了Netty服务,在response返回的时候,有时候会写的数据是不全的,于是我在实体类中新增了一个setAppendResponse方法进行拼接, 再者,gateway的过滤器是链式结构,需要定义order排序为最先(-1),然后和预置的gateway过滤器做一个combine.

代码中用到的 dataBuffer 结构,底层其实也是类似netty的byteBuffer,用到了字节数组池,同时也用到了 引用计数器 (refInt).

为了让jvm在gc的时候垃圾得到回收,避免内存泄露,我们需要在转换字节使用的地方,显示的释放一次

DataBufferUtils.release(dataBuffer);

到此,相信大家对“Spring Cloud gateway网关怎么拦截Post请求日志”有了更深的了解,不妨来实际操作一番吧!这里是编程网网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Spring Cloud gateway网关怎么拦截Post请求日志

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Spring Cloud gateway网关怎么拦截Post请求日志

本篇内容主要讲解“Spring Cloud gateway网关怎么拦截Post请求日志”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Spring Cloud gateway网关怎么拦截Post请
2023-06-20

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录