SpringCloud Gateway(四)定制请求参数日志输出

Gateway
placeholder image
admin 发布于:2023-01-29 18:21:11
阅读:loading

背景介绍

其实在学习SpringCloud Gateway的过程中一直有在琢磨写个什么示例来证明一下这玩意我是懂一点的呢,也是反复琢磨和反复实践最终折腾出来的结果是编写过滤器获取一个http请求的所有参数,包含基本参数、Request Header、Response Header、Response等信息,说的高端一点就是拿到浏览器开发者工具F12的信息,所以我们先来看看一个复杂的F12请求包含了哪些类型的参数信息,如下图所示:

image.png

通过上图可以看到在一个http请求中有意义获取到的信息部分有:General、Request Headers、Response Headers、Qhery String Parameters、Response,有的http请求中参数使用Request Body传递的,这部分数据在Payload部分,所以在编写示例的时候专门定义了一个Http请求参数范围的枚举来按照范围输出请求的参数信息,枚举的实例范围为:All、General、RequestHeaders、ResponseHeaders、QueryStringParameters、Payload、Response。

示例流程的逻辑比较简单,从用户请求开始到达Gateway网关后,先由路由谓词工厂匹配,再由局部过滤器设置请求输出的参数范围(如上设置了7种范围),最后由全局过滤器进行参数输出,也算是为了同时研究全局过滤器和局部过滤器吧,本例的示例流程图如下所示:

image.png

参考代码

(1)GatewayContext数据对象

package cn.chendd.cofig.filters;

import org.springframework.util.MultiValueMap;

/**
 * @author chendd
 * @date 2023/1/1 10:23
 */
public class GatewayContext {

    /**
     * 缓存request body的key
     */
    public static final String REQUEST_BODY_CACHE_GATEWAY_CONTEXT = "REQUEST_BODY_CACHE_GATEWAY_CONTEXT";

    /**
     * 记录请求参数日志的范围名称
     */
    public static final String HTTP_PARAM_SCOPE = "HTT_PARAM_SCOPE";

    /**
     * 请求1MB的字节大小
     */
    public static final int LOG_UPLOAD_SIZE = 1024 * 1024;

    /**
     * requestBody记录日志的最大字节大小,小于此参数的请求记录参数信息,否则输出文件地址
     */
    public static final int LOG_UPLOAD_MAX_SIZE = LOG_UPLOAD_SIZE * 5;

    /**
     * 请求body
     */
    private String requestBody;

    /**
     * 响应body
     */
    private String responseBody;

    /**
     * form数据
     */
    private MultiValueMap<String, String> formData;

   //getter setter
}

(2)局部过滤器

@Component
public class HttpParamScopeGatewayFilterFactory extends AbstractGatewayFilterFactory<HttpParamScopeGatewayFilterFactory.Config> implements Ordered {

    public HttpParamScopeGatewayFilterFactory() {
        super(Config.class);
    }

    @Override
    public GatewayFilter apply(HttpParamScopeGatewayFilterFactory.Config config) {
        //局部过滤器,获取配置的日志参数范围
        return (exchange, chain) -> chain.filter(exchange).doFinally(item -> {

            exchange.getAttributes().put(GatewayContext.HTTP_PARAM_SCOPE , config.getScopes());
        });
    }

    @Override
    public int getOrder() {
        return -90;
    }
}

(3)全局过滤器

@Component
public class GatewayContextFilter implements GlobalFilter, Ordered {

    @Resource
    private ServerCodecConfigurer codecConfigurer;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        MediaType mediaType = exchange.getRequest().getHeaders().getContentType();
        if (mediaType == null || MediaType.APPLICATION_FORM_URLENCODED.isCompatibleWith(mediaType)) {
            GatewayContext gatewayContext = new GatewayContext();
            exchange.getAttributes().put(GatewayContext.REQUEST_BODY_CACHE_GATEWAY_CONTEXT, gatewayContext);
            return readFormData(exchange, chain, gatewayContext);
        }
        HttpHeaders headers = exchange.getRequest().getHeaders();
        MediaType contentType = headers.getContentType();
        long contentLength = headers.getContentLength();
        //处理包含请求体的逻辑
        if (contentLength > 0) {
            //处理请求
            GatewayContext gatewayContext = new GatewayContext();
            exchange.getAttributes().put(GatewayContext.REQUEST_BODY_CACHE_GATEWAY_CONTEXT, gatewayContext);
            if (MediaType.APPLICATION_JSON.isCompatibleWith(contentType)) {
                return readBody(exchange, chain, gatewayContext);
            }
            //若为文件上传
            if (MediaType.MULTIPART_FORM_DATA.isCompatibleWith(contentType)) {
                return readBody(exchange, chain, gatewayContext);
            }
        }
        //处理json无请求体的请求
        if (MediaType.APPLICATION_JSON.isCompatibleWith(contentType)) {
            GatewayContext gatewayContext = new GatewayContext();
            exchange.getAttributes().put(GatewayContext.REQUEST_BODY_CACHE_GATEWAY_CONTEXT, gatewayContext);
            return readFormData(exchange, chain, gatewayContext);
        }
        return this.doChainFilter(exchange , chain , null);
    }

    @Override
    public int getOrder() {
        return -100;
    }
}

运行结果

(1)测试参数列表

image.png


(9种参数传递)

(2)简单地址参数

image.png

(路径变量的参数输出)

(3)多范围参数

image.png

image.png

(多种参数传递输出验证)

(4)含文件上传参数

image.png

image.png

(文件上传和多参数提交)

(5)其它输出

image.png

(文件下载)

特别说明

(1)以上示例代码只是主要代码的片段,源码示例见:源码下载.txt

(2)本示例有专门针对于多种参数传递进行输出,场景如下:

A.获取在URL中使用?传递参数的形式;

B.获取Request Body传递参数的形式;

C.获取Request Post传递参数的形式;

D.获取路径地址信息(含路径变量)的形式;

E.获取使用MULTIPART_FORM_DATA文件上传请求中的参数,若上传文件大于5M则输出《文件名称》,否则输出文件内容;

F.获取Response结果响应信息,若当前是文件输出流则只输出《文件名称等信息;



 点赞


 发表评论

当前回复:作者

 评论列表


留言区