gateway过滤器中实现记录访问日志

这篇具有很好参考价值的文章主要介绍了gateway过滤器中实现记录访问日志。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、需求分析

SpringCloud多服务项目环境,前端请求经过网关中转发到各个服务节点。日志中需要记录请求头中的部分参数、请求的body、响应状态及响应内容,并在请求头中新增一个标识。

二、代码实现

1. 装饰Request


@Component
public class RequestFilter implements GlobalFilter, Ordered {

    /**
     * 1.封装 HttpRequest,使 Request body 可以重复读取;
     * 2.封装 HttpRequestHeader,添加requestId;
     */

    @Override
    public int getOrder() {
        // 最高优先级的执行顺序
        return HIGHEST_PRECEDENCE;
    }
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // Request.
        ServerHttpRequest request = exchange.getRequest();

        // 新建Header 添加 requestId.
        String requestId = new SnowFlakeId().get();
        request.mutate().headers(httpHeaders -> {
            httpHeaders.set("requestId", requestId);
        }).build();

        if (Objects.requireNonNull(request.getMethod()).equals(HttpMethod.POST)) {
            // body 为null, 不会执行里面的重分派.
            DefaultDataBufferFactory defaultDataBufferFactory = new DefaultDataBufferFactory();
            DefaultDataBuffer defaultDataBuffer = defaultDataBufferFactory.allocateBuffer(0);
            //构建新数据流, 当body为空时,构建空流
            Flux<DataBuffer> bodyDataBuffer = exchange.getRequest().getBody().defaultIfEmpty(defaultDataBuffer);

            return DataBufferUtils.join(bodyDataBuffer)
                    .flatMap(dataBuffer -> {
                        // Request.
                        DataBufferUtils.retain(dataBuffer);
                        Flux<DataBuffer> cachedFlux = Flux.defer(() -> Flux.just(dataBuffer.slice(0, dataBuffer.readableByteCount())));
                        ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator(exchange.getRequest()) {
                            @Override
                            public Flux<DataBuffer> getBody() {
                                return cachedFlux;
                            }
                        };

                        return chain.filter(exchange.mutate().request(mutatedRequest).build());
                    });
        }
        else {
            return chain.filter(exchange);
        }
    }

}

PS:

1.Order 最高优先级。

2.Request Header 不可添加值,需重新创建后绑定。

3.Request Body IO流,读取一次后就空了;新建一个Request绑定到请求中。

4. POST请求,Body为空时,没有执行新建Request,需要构建一个空的输入流使新建Request可以执行。

2. 装饰Response


@Component
@Slf4j
@AllArgsConstructor
public class ResponseFilter implements GlobalFilter, Ordered {
    
    /**
     * 1.封装HttpResponse,读取Respond 内容;
     * 2.记录操作日志:请求信息、响应状态、响应信息等;
     */

    @Override
    public int getOrder() {
        // before write response.
        return NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER - 1;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // Response
        ServerHttpResponse mutatedResponse = responseDecorator(exchange);

        return chain.filter(exchange.mutate()
                .response(mutatedResponse)
                .build());
    }

    public ServerHttpResponse responseDecorator(ServerWebExchange exchange) {
        ServerHttpResponse originalResponse = exchange.getResponse();
        DataBufferFactory bufferFactory = originalResponse.bufferFactory();
        return new ServerHttpResponseDecorator(originalResponse) {
            @Override
            public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {

                // 读取 Response 的响应流中的信息后,再写回去
                if (body instanceof Flux) {
                    // 获取ContentType,判断是否返回JSON格式数据
                    String originalResponseContentType = exchange.getAttribute(ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR);
                    if (StringUtils.isNotBlank(originalResponseContentType) && originalResponseContentType.contains(APPLICATION_JSON)) {
                        Flux<? extends DataBuffer> fluxBody = Flux.from(body);

                        return super.writeWith(fluxBody.buffer().map(dataBuffers -> {
                            // Response Content.
                            // String responseContent = resolveContentFromResponse(dataBuffers, bufferFactory);
                            DataBuffer join = bufferFactory.join(dataBuffers);
                            byte[] content = new byte[join.readableByteCount()];
                            join.read(content);
                            DataBufferUtils.release(join);

                            String responseContent = formatStr(new String(content, StandardCharsets.UTF_8));


                            // 添加操作日志.
                            ServerHttpRequest request = exchange.getRequest();
                            String apiPath = request.getPath().toString();
                            HttpHeaders headers = request.getHeaders();
                            String requestBody = resolveBodyFromRequest(request.getBody());

                            appendSystemLog(apiPath, headers, requestBody, responseContent, getStatusCode());

                            // 写回Response Content.
                            // String 转为 byte[]后再写入DataBuffer,可能出现中文全部为?的情况
                            // byte[] uppedContent = new String(responseContent.getBytes(), StandardCharsets.UTF_8).getBytes();
                            // originalResponse.getHeaders().setContentLength(uppedContent.length);
                            return bufferFactory.wrap(content);
                        }));
                    }
                }
                return super.writeWith(body);
            }

            @Override
            public Mono<Void> writeAndFlushWith(Publisher<? extends Publisher<? extends DataBuffer>> body) {
                return writeWith(Flux.from(body).flatMapSequential(p -> p));
            }
        };
    }

    /**
     * 解析 RequestBody
     */
    public String resolveBodyFromRequest(Flux<DataBuffer> body) {
        AtomicReference<String> bodyRef = new AtomicReference<>();
        // 缓存读取的request body信息
        body.subscribe(dataBuffer -> {
            CharBuffer charBuffer = StandardCharsets.UTF_8.decode(dataBuffer.asByteBuffer());
            DataBufferUtils.release(dataBuffer);
            bodyRef.set(charBuffer.toString());
        });
        return formatStr(bodyRef.get());
    }

    /**
     * 解析 ResponseBody
     */
    public String resolveContentFromResponse(List<? extends DataBuffer> dataBuffers, DataBufferFactory bufferFactory) {
        //(返回数据内如果字符串过大,默认会切割)解决返回体分段传输
        // 遍历 List时,拼接字符串会遇到汉字乱码的现象。应全部读取后再转成字符串。
        DataBuffer join = bufferFactory.join(dataBuffers);
        byte[] content = new byte[join.readableByteCount()];
        join.read(content);
        DataBufferUtils.release(join);

        return formatStr(new String(content, StandardCharsets.UTF_8));
    }


    /**
     * 去掉空格,换行和制表符
     */
    private String formatStr(String str){
        if (str != null && str.length() > 0) {
            Pattern p = Pattern.compile("\t|\r|\n");
            Matcher m = p.matcher(str);
            return m.replaceAll("");
        }
        return str;
    }

    /**
     * 记录操作日志
     */
    public void appendSystemLog(String apiPath, HttpHeaders headers, String requestBody, String responseBody, HttpStatus status) {
        try {
            // todo 记录日志
            // service 
        } catch (Exception e) {
            log.error("==== Append SystemLog error.", e);
        }
    }

}

PS:

1.Order 需要在Response内容被NettyWriteResponseFilter cleanup之前读取响应类容和RequestBody。

2. 读取Response内容时,需要注意由于相容内容分段(List),导致读取时阶段汉字的bytes出现个别汉字乱码。

3.Response内容读取后回写。文章来源地址https://www.toymoban.com/news/detail-619098.html

到了这里,关于gateway过滤器中实现记录访问日志的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Spring Cloud Gateway 过滤器

    Spring Cloud Gateway 过滤器的种类有30多种。 官文文档地址: Spring Cloud Gateway https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gatewayfilter-factories Spring Cloud Gateway大体可以分为下面两种类型的过滤器: 1、内置的过滤器         1.1、内置的局部过滤器         1.2、内置的全

    2024年03月28日
    浏览(59)
  • gateway过滤器没生效,特殊原因

    看这边文章的前提,你要会gateway,知道过滤器怎么配置? 直接来看过滤器,局部过滤器 再来看配置 请求路径 http://127.0.0.1:8080/appframework/services/catalog/catalogSpecials.json?pageindex=1pagesize=10pkid=d9873700ef7e42b3b8f4e782f345975b 看起来确实没什么问题 注意: 我这里还有个应用,就是网关转

    2024年02月14日
    浏览(52)
  • gateway之过滤器(Filter)详解

    在Spring Cloud中,过滤器(Filter)是一种关键的组件,用于在微服务架构中处理和转换传入请求以及传出响应。过滤器位于服务网关或代理中,并通过拦截请求和响应流量来提供各种功能。 过滤器在请求的不同生命周期阶段执行特定的操作,例如鉴权、认证、请求转发、限流、

    2024年02月05日
    浏览(44)
  • Spring Cloud GateWay 全局过滤器

    这是一个自定义的 Spring Cloud Gateway 全局过滤器(Global Filter)。在 Spring Cloud Gateway 中,全局过滤器可以在请求被路由到目标服务之前或之后执行一些操作。这个过滤器实现了 GlobalFilter 接口和 Ordered 接口,这两个接口的作用如下: GlobalFilter 接口: 这是一个 Spring Cloud Gateway 提

    2024年02月11日
    浏览(46)
  • Spring Cloud Gateway 过滤器详解

    Spring Cloud Gateway根据作用范围划分为:GatewayFilter和GlobalFilter 由filter工作流程点,可以知道filter有着非常重要的作用,在“pre”类型的过滤器可以做参数校验、权限校验、流量监控、日志输出、协议转换等,在“post”类型的过滤器中可以做响应内容、响应头的修改,日志的输

    2023年04月08日
    浏览(39)
  • 178 服务网关GateWay---局部过滤器(GatewayFilter)

    在微服务架构下,后端微服务可能都需要做一些重复相同的事情,如:认证鉴权、限流、黑白名单、日志收集等。对于这些需求当有了网关之后,就可以统一的在网关中实现即可,避免代码冗余。 ​ 要实现这些功能,则需要通过网关的过滤器进行实现。网关的过滤器类型分

    2024年02月21日
    浏览(50)
  • 云原生之 Gateway 的 Filter 过滤器

    通常情况下,出于安全方面的考虑, 服务端提供的服务往往都会有一定的校验逻辑 ,例如用户登陆状态校验、签名校验等。 在微服务架构中,系统由多个微服务组成,所有这些服务都需要这些校验逻辑,此时我们就可以将 这些校验逻辑写到 Spring Cloud Gateway 的 Filter 过滤器中

    2023年04月14日
    浏览(51)
  • GateWay具体的使用之局部过滤器接口耗时

    局部过滤器命名规则 XXX GatewayFilterFactory , 必须以 GatewayFilterFactory 结尾。

    2024年04月28日
    浏览(35)
  • Spring Cloud Gateway快速入门(三)——过滤器

    Gateway过滤器是Spring Cloud Gateway提供的一种机制,用于对进入网关的请求和返回进行处理和转换。它可以用于实现各种功能,如请求鉴权、请求转发、请求限流、请求重试等。 网关过滤器是Spring Cloud Gateway提供的一种机制,用于在请求进入网关和响应离开网关时进行一些预处理

    2024年02月04日
    浏览(47)
  • Spring Cloud Gateway过滤器GlobalFilter详解

    一、过滤器的场景 在springCloud架构中,网关是必不可少的组件,它用于服务路由的转发。对客户端进行屏蔽微服务的具体细节,客户端只需要和网关进行交互。所以网关顾名思义,就是网络的一个关卡。它就是一座城的城门守卫。所以这个守卫就可以做很多工作,比如对来访

    2024年02月14日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包