不是基于springmvc的,而是基于webflux去做的
SpringCloudGateway中Postapp开发定制请求参数只能读取一次
这是因为Gatewayapp开发定制默认使用的是SpringWebflux,app开发定制解决这个问题需要容重新构造一个request来替换原先的request
全局过滤器把原有的request请求中的body内容读出来,并且使用ServerHttpRequestDecorator这个请求装饰器对request进行包装,重写getBody方法,并把包装后的请求放到过滤器链中传递下去。
//这样后面的过滤器中再使用exchange.getRequest().getBody()来获取body时,实际上就是调用的重载后的getBody方法,获取的最先已经缓存了的body数据。这样就能够实现body的多次读取了。
//过滤器的Ordered.HIGHEST_PRECEDENCE,即最高优先级的过滤器。优先级设置高的原因是某些系统内置的过滤器可能也会去读body。
Request log
@Configurationpublic class RequestLogGlobalFilter implements GlobalFilter, Ordered { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpRequest request = exchange.getRequest(); URI uri = request.getURI(); //String path = request.getPath().value(); String path = request.getPath().pathWithinApplication().value();//打印请求路径 String requestUrl = this.getOriginalRequestUrl(exchange);//打印请求url String method = request.getMethodValue(); //cors HttpHeaders headers = request.getHeaders(); // if(CorsUtils.isCorsRequest(request)){// log.info("---> isCorsRequest Access");// headers.add("Access-Control-Allow-Origin", "*");// headers.add("Access-Control-Allow-Methods", "*");// headers.add("Access-Control-Max-Age", "18000L");// headers.add("Access-Control-Allow-Headers", "*");// headers.add("Access-Control-Expose-Headers", "*");// headers.add("Access-Control-Allow-Credentials", "true");// } log.info("---> method: {} URI: {} header: {}", method, requestUrl, headers); if ("POST".equals(method)) { return DataBufferUtils.join(exchange.getRequest().getBody()) .flatMap(dataBuffer -> { byte[] bytes = new byte[dataBuffer.readableByteCount()]; dataBuffer.read(bytes); String bodyString = new String(bytes, StandardCharsets.UTF_8); log.info("---> {}", bodyString); exchange.getAttributes().put("POST_BODY", bodyString); DataBufferUtils.release(dataBuffer); Flux<DataBuffer> cachedFlux = Flux.defer(() -> { DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(bytes); return Mono.just(buffer); }); ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator(request) { @Override public Flux<DataBuffer> getBody() { return cachedFlux; } }; //log.info("****************************************************************************\"); return chain.filter(exchange.mutate().request(mutatedRequest).build()); }); } else if ("GET".equals(method)) { MultiValueMap<String, String> queryParams = request.getQueryParams(); log.info("请求参数:" + queryParams); //log.info("****************************************************************************\"); return chain.filter(exchange); } return chain.filter(exchange); } private String getOriginalRequestUrl(ServerWebExchange exchange) { ServerHttpRequest req = exchange.getRequest(); LinkedHashSet<URI> uris = exchange.getRequiredAttribute(ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR); URI requestUri = uris.stream().findFirst().orElse(req.getURI()); MultiValueMap<String, String> queryParams = req.getQueryParams(); //打印 /api/rest/feign/order/detail // return UriComponentsBuilder.fromPath(requestUri.getRawPath()).queryParams(queryParams).build().toUriString(); return requestUri.toString(); // http://localhost:8091/api/rest/feign/order/detail } @Override public int getOrder() { return Ordered.LOWEST_PRECEDENCE; }// @Bean //https://www.pudn.com/news/6250847e74bc5c01056623c1.html// public CorsWebFilter corsWebFilter() {// UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource (new PathPatternParser());// CorsConfiguration corsConfig = new CorsConfiguration ();// // 允许所有请求方法// corsConfig.addAllowedMethod ("*");// // 允许所有域,当请求头// corsConfig.addAllowedOrigin ("*");// // 允许全部请求头// corsConfig.addAllowedHeader ("*");// // 允许携带 Cookie 等用户凭证// corsConfig.setAllowCredentials (true);// // 允许全部请求路径// source.registerCorsConfiguration ("/**", corsConfig);// return new CorsWebFilter (source);// }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
Response log
@Configurationpublic class ResponseLogGlobalFilter implements GlobalFilter, Ordered { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { try { ServerHttpResponse originalResponse = exchange.getResponse(); DataBufferFactory bufferFactory = originalResponse.bufferFactory(); HttpStatus statusCode = originalResponse.getStatusCode(); if(statusCode == HttpStatus.OK){ ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) { @Override public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) { //log.info("body instanceof Flux: {}", (body instanceof Flux)); if (body instanceof Flux) { Flux<? extends DataBuffer> fluxBody = Flux.from(body); // return super.writeWith(fluxBody.map(dataBuffer -> { byte[] content = new byte[dataBuffer.readableByteCount()]; dataBuffer.read(content); DataBufferUtils.release(dataBuffer);//释放掉内存 // 构建日志 StringBuilder sb2 = new StringBuilder(200); sb2.append("<--- {} {} \"); List<Object> rspArgs = new ArrayList<>(); rspArgs.add(originalResponse.getStatusCode()); //rspArgs.add(requestUrl); String data = new String(content, StandardCharsets.UTF_8);//data sb2.append(data); log.info(sb2.toString(), rspArgs.toArray());//log.info("<-- {} {}\", originalResponse.getStatusCode(), data); return bufferFactory.wrap(content); })); } else { log.error("<--- {} 响应code异常", getStatusCode()); } return super.writeWith(body); } }; return chain.filter(exchange.mutate().response(decoratedResponse).build()); } return chain.filter(exchange);//降级处理返回数据 }catch (Exception e){ log.error("gateway log exception.\" + e); return chain.filter(exchange); } } @Override public int getOrder() { return Ordered.HIGHEST_PRECEDENCE; }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
SpringCloud Gateway 请求响应日志打印
参考资料
SpringCloud Gateway 打印请求和响应信息
https://blog.csdn.net/liben0429/article/details/126106528
Gateway网关日志
https://blog.csdn.net/small_to_large/article/details/125326498
https://www.jb51.net/article/240256.htm
https://blog.csdn.net/chaojunma/article/details/122595612
http://t.zoukankan.com/lifengdi-p-12524092.html
https://www.51ufo.cn/%E5%88%86%E5%B8%83%E5%BC%8F/2021/12/23/SpringCloud-Gateway%E7%BD%91%E5%85%B3%E6%94%B6%E9%9B%86%E8%AF%B7%E6%B1%82%E6%97%A5%E5%BF%97.html
乱码问题处理
https://blog.csdn.net/qq_38380025/article/details/100032490
https://www.icode9.com/content-4-1302983.html