iis服务器助手广告广告
返回顶部
首页 > 资讯 > 精选 >Spring Cloud gateway网关怎么拦截Post请求日志
  • 712
分享到

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

2023-06-20 16:06:27 712人浏览 独家记忆
摘要

本篇内容主要讲解“spring cloud gateway网关怎么拦截Post请求日志”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“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).

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

DataBufferUtils.release(dataBuffer);

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

--结束END--

本文标题: Spring Cloud gateway网关怎么拦截Post请求日志

本文链接: https://www.lsjlt.com/news/298188.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

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

下载Word文档
猜你喜欢
  • Spring Cloud gateway网关怎么拦截Post请求日志
    本篇内容主要讲解“Spring Cloud gateway网关怎么拦截Post请求日志”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Spring Cloud gateway网关怎么拦截Post请...
    99+
    2023-06-20
  • Spring Cloud gateway 网关如何拦截Post请求日志
    gateway版本是 2.0.1 1.pom结构 (部分内部项目依赖已经隐藏) <dependency> <groupId>org.springf...
    99+
    2024-04-02
  • 微信小程序怎么封装网络请求和拦截器
    这篇文章主要介绍“微信小程序怎么封装网络请求和拦截器”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“微信小程序怎么封装网络请求和拦截器”文章能帮助大家解决问题。1. 前言今天和合作伙伴对接代码的时候,...
    99+
    2023-07-05
  • Spring cloud网关gateway进行websocket路由转发规则怎么配置
    这篇“Spring cloud网关gateway进行websocket路由转发规则怎么配置”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下...
    99+
    2023-07-05
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作