java getway springcloud 记录请求数据

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("\n"));
            log.info("\n" + "-------------------------------------------------------------------------------->>\n" +
                            "locationtype: request\n" +
                            "Url: {}\n" +
                            "HttpMethod: {}\n" +
                            "IP: {}\n" +
                            "accessId: {}\n" +
                            "Param: {}\n" +
                            "Headers: \n" +
                            "{}\n" +
                            "\"<<--------------------------------------------------------------------------------"
                    , 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>

 

参考原来

pringCloud-Gateway获取body参数,解决只能获取一次问题,终极解决方案

posted @ 2021-08-15 15:45  ☆♂安♀★  阅读(284)  评论(0编辑  收藏  举报