java getway springcloud 记录请求数据,兼容post请求无参数和response返回无参数
方式1
import com.icar.web.icarutils.util.ClientUtil; import com.icar.web.icarutils.util.IdWorkerUtil; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; 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.MediaType; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpRequestDecorator; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import java.io.UnsupportedEncodingException; import java.util.Map; import java.util.stream.Collectors; @Component @Slf4j public class HttpRequestGlobalFilter implements GlobalFilter, Ordered { @Value("${xxxx.collectrequestlogs:false}") private Boolean collectrequestlog; @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpRequest request = exchange.getRequest(); String accessId=new IdWorkerUtil().nextId()+""; try{ String method = request.getMethodValue(); String contentType = request.getHeaders().getFirst("Content-Type"); if ("POST".equals(method)&&(contentType!=null&&contentType.equals(MediaType.APPLICATION_JSON_VALUE))){ //&& contentType.startsWith("multipart/form-data") if(collectrequestlog){ return SavePostRequestDataBody(exchange, chain,request,method,contentType,accessId); } else { SaveLogData(exchange,"",accessId); } } else if ("GET".equals(method)) { Map requestQueryParams = request.getQueryParams(); String param=requestQueryParams.toString(); SaveLogData(exchange,param,accessId); }else{ SaveLogData(exchange,"",accessId); } ServerHttpRequest.Builder requestBuilder = request.mutate(); //requestBuilder.build().getHeaders().set("userId",userId); requestBuilder.headers(k -> k.set("accessId", accessId)); ServerHttpRequest builder = requestBuilder.build(); return chain.filter(exchange.mutate().request(builder.mutate().build()).build()); } catch (Exception e){ log.error("",e); } ServerHttpRequest.Builder requestBuilder = request.mutate(); //requestBuilder.build().getHeaders().set("userId",userId); requestBuilder.headers(k -> k.set("accessId", accessId)); ServerHttpRequest builder = requestBuilder.build(); return chain.filter(exchange.mutate().request(builder.mutate().build()).build()); } /** * 获取post请求数据 * @param exchange * @param method * @param contentType * @param accessId * @return */ private Mono<Void> SavePostRequestDataBody(ServerWebExchange exchange, GatewayFilterChain chain, ServerHttpRequest request, String method, String contentType, String accessId) { //判断请求是否有参数 HttpHeaders headers = request.getHeaders(); long contentLength = headers.getContentLength(); //当请求体里面没有任何数据的话不用后面的获取数据步骤 if (contentLength < 1) { SaveLogData(exchange,"",accessId); ServerHttpRequest.Builder requestBuilder = request.mutate(); //requestBuilder.build().getHeaders().set("userId",userId); requestBuilder.headers(k -> k.set("accessId", accessId)); ServerHttpRequest builder = requestBuilder.build(); return chain.filter(exchange.mutate().request(builder.mutate().build()).build()); } Flux<DataBuffer> flux =exchange.getRequest().getBody(); Mono<Void> mono= DataBufferUtils.join(flux) .flatMap(dataBuffer -> { try { byte[] bytes = new byte[dataBuffer.readableByteCount()]; dataBuffer.read(bytes); try { if(contentType==null||!contentType.startsWith("application/json")){ //multipart/form-data SaveLogData(exchange,"",accessId); }else{ String bodyString = new String(bytes, "utf-8"); SaveLogData(exchange,bodyString,accessId); } //log.info(bodyString); //exchange.getAttributes().put("POST_BODY",bodyString); } catch (UnsupportedEncodingException e) { log.error("",e); } DataBufferUtils.release(dataBuffer); Flux<DataBuffer> cachedFlux = Flux.defer(() -> { DataBuffer buffer = exchange.getResponse().bufferFactory() .wrap(bytes); return Mono.just(buffer); }); ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator( exchange.getRequest()) { @Override public Flux<DataBuffer> getBody() { return cachedFlux; } }; ServerHttpRequest.Builder requestBuilder = mutatedRequest.mutate(); requestBuilder.headers(k -> k.set("accessId", accessId)); ServerHttpRequest builder = requestBuilder.build(); return chain.filter(exchange.mutate().request(mutatedRequest) .build()); } catch (Exception e){ SaveLogData(exchange,"",accessId); log.error("",e); }finally { } return null; }); return mono; } private void SaveLogData(ServerWebExchange exchange,String param,String accessId) { try{ ServerHttpRequest request= exchange.getRequest(); String ip = ClientUtil.getIPAddress(request); String method = request.getMethodValue(); String url = request.getURI().toString(); String headers = request.getHeaders().entrySet() .stream() .map(entry -> " " + entry.getKey() + ": [" + String.join(";", entry.getValue()) + "]") .collect(Collectors.joining(" ")); log.info(" " + "-------------------------------------------------------------------------------->> " + "locationtype: request " + "Url: {} " + "HttpMethod: {} " + "IP: {} " + "accessId: {} " + "Param: {} " + "Headers: " + "{} " + ""<<--------------------------------------------------------------------------------" , method, url, ip,accessId,param, headers); } catch (Exception e){ log.error("",e); } } @Override public int getOrder() { return -200; } }
方式二
import lombok.Data; import org.springframework.http.HttpHeaders; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; /** * 网关上下文 */ @Data public class GatewayContext { public static final String CACHE_GATEWAY_CONTEXT = "cacheGatewayContext"; /** * cache headers */ private HttpHeaders headers; /** * baseHeader */ private HttpHeaders baseHeader; /** * cache json body */ private String cacheBody; /** * cache formdata */ private MultiValueMap<String, String> formData = new LinkedMultiValueMap<>(); /** * ipAddress */ private String ipAddress; /** * path */ private String path; }
import com.icar.web.gateway.entity.GatewayContext; import io.netty.buffer.ByteBufAllocator; import lombok.extern.slf4j.Slf4j; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.Ordered; import org.springframework.core.io.ByteArrayResource; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.io.buffer.DataBufferUtils; import org.springframework.core.io.buffer.NettyDataBufferFactory; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.codec.HttpMessageReader; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpRequestDecorator; import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.stereotype.Component; import org.springframework.util.MultiValueMap; import org.springframework.web.reactive.function.server.HandlerStrategies; import org.springframework.web.reactive.function.server.ServerRequest; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Map; /** * 请求内容存储 处理请求内容 内容放在gatewayContext中 *//* * @author kam * * <p> * 请求内容存储 处理请求内容 内容放在gatewayContext中 * </p> */ @Component @Slf4j public class RequestCoverFilter implements GlobalFilter, Ordered { /** * default HttpMessageReader */ private static final List<HttpMessageReader<?>> messageReaders = HandlerStrategies.withDefaults().messageReaders(); /* * ReadFormData * * @param exchange * @param chain * @return */ private Mono<Void> readFormData(ServerWebExchange exchange, GatewayFilterChain chain, GatewayContext gatewayContext) { final ServerHttpRequest request = exchange.getRequest(); HttpHeaders headers = request.getHeaders(); return exchange.getFormData().doOnNext(multiValueMap -> { gatewayContext.setFormData(multiValueMap); log.debug("[GatewayContext]Read FormData:{}", multiValueMap); }).then(Mono.defer(() -> { Charset charset = headers.getContentType().getCharset(); charset = charset == null ? StandardCharsets.UTF_8 : charset; String charsetName = charset.name(); MultiValueMap<String, String> formData = gatewayContext.getFormData(); /** * formData is empty just return */ if (null == formData || formData.isEmpty()) { return chain.filter(exchange); } StringBuilder formDataBodyBuilder = new StringBuilder(); String entryKey; List<String> entryValue; try { /** * repackage form data */ for (Map.Entry<String, List<String>> entry : formData.entrySet()) { entryKey = entry.getKey(); entryValue = entry.getValue(); if (entryValue.size() > 1) { for (String value : entryValue) { formDataBodyBuilder.append(entryKey).append("=") .append(URLEncoder.encode(value, charsetName)).append("&"); } } else { formDataBodyBuilder.append(entryKey).append("=") .append(URLEncoder.encode(entryValue.get(0), charsetName)).append("&"); } } } catch (UnsupportedEncodingException e) { // ignore URLEncode Exception } /** * substring with the last char '&' */ String formDataBodyString = ""; if (formDataBodyBuilder.length() > 0) { formDataBodyString = formDataBodyBuilder.substring(0, formDataBodyBuilder.length() - 1); } /** * get data bytes */ byte[] bodyBytes = formDataBodyString.getBytes(charset); int contentLength = bodyBytes.length; ServerHttpRequestDecorator decorator = new ServerHttpRequestDecorator(request) { /** * change content-length * * @return */ @Override public HttpHeaders getHeaders() { HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.putAll(super.getHeaders()); if (contentLength > 0) { httpHeaders.setContentLength(contentLength); } else { httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked"); } return httpHeaders; } /** * read bytes to Flux<Databuffer> * * @return */ @Override public Flux<DataBuffer> getBody() { return DataBufferUtils.read(new ByteArrayResource(bodyBytes), new NettyDataBufferFactory(ByteBufAllocator.DEFAULT), contentLength); } }; ServerWebExchange mutateExchange = exchange.mutate().request(decorator).build(); log.info("[GatewayContext]Rewrite Form Data :{}", formDataBodyString); return chain.filter(mutateExchange); })); } /** * ReadJsonBody * * @param exchange * @param chain * @return */ private Mono<Void> readBody(ServerWebExchange exchange, GatewayFilterChain chain, GatewayContext gatewayContext) { /** * join the body */ return DataBufferUtils.join(exchange.getRequest().getBody()).flatMap(dataBuffer -> { /* * read the body Flux<DataBuffer>, and release the buffer * //TODO when SpringCloudGateway Version Release To G.SR2,this can be update with the new version's feature * see PR https://github.com/spring-cloud/spring-cloud-gateway/pull/1095 */ byte[] bytes = new byte[dataBuffer.readableByteCount()]; dataBuffer.read(bytes); DataBufferUtils.release(dataBuffer); Flux<DataBuffer> cachedFlux = Flux.defer(() -> { DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(bytes); DataBufferUtils.retain(buffer); return Mono.just(buffer); }); /** * repackage ServerHttpRequest */ ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator(exchange.getRequest()) { @Override public Flux<DataBuffer> getBody() { return cachedFlux; } }; /** * mutate exchage with new ServerHttpRequest */ ServerWebExchange mutatedExchange = exchange.mutate().request(mutatedRequest).build(); /** * read body string with default messageReaders */ return ServerRequest.create(mutatedExchange, messageReaders).bodyToMono(String.class) .doOnNext(objectValue -> { gatewayContext.setCacheBody(objectValue); log.debug("[GatewayContext]Read JsonBody:{}", objectValue); }).then(chain.filter(mutatedExchange)); }); } @Override public int getOrder() { return HIGHEST_PRECEDENCE; } @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { /** * save request path and serviceId into gateway context */ ServerHttpRequest request = exchange.getRequest(); ServerHttpResponse response = exchange.getResponse(); GatewayContext gatewayContext = new GatewayContext(); String path = request.getPath().pathWithinApplication().value(); gatewayContext.setPath(path); gatewayContext.getFormData().addAll(request.getQueryParams()); gatewayContext.setIpAddress(String.valueOf(request.getRemoteAddress())); HttpHeaders headers = request.getHeaders(); gatewayContext.setHeaders(headers); log.debug("HttpMethod:{},Url:{}", request.getMethod(), request.getURI().getRawPath()); /// 注意,因为webflux的响应式编程 不能再采取原先的编码方式 即应该先将gatewayContext放入exchange中,否则其他地方可能取不到 /** * save gateway context into exchange */ exchange.getAttributes().put(GatewayContext.CACHE_GATEWAY_CONTEXT, gatewayContext); // 处理参数 MediaType contentType = headers.getContentType(); long contentLength = headers.getContentLength(); if (contentLength > 0) { if (MediaType.APPLICATION_JSON.equals(contentType) || MediaType.APPLICATION_JSON_UTF8.equals(contentType)) { return readBody(exchange, chain, gatewayContext); } if (MediaType.APPLICATION_FORM_URLENCODED.equals(contentType)) { return readFormData(exchange, chain, gatewayContext); } } // TODO 多版本划区域控制后期实现 log.debug("[GatewayContext]ContentType:{},Gateway context is set with {}", contentType, gatewayContext); return chain.filter(exchange); } }
spring-cloud的版本
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.1.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Hoxton.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>2.2.1.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
参考原来