Spring Cloud Gateway 3 全局过滤器中修改响应体案例 1

使用场景

当服务返回错误时希望通过网关修改为统一的错误格式;对返回数据统一加解密等。

  • 获取服务响应码,如果异常就修改响应体
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONWriter;
import com.xxx.commons.model.response.ResponseEnum;
import com.xxx.commons.model.response.ServerResponseEntity;
import com.xxx.commons.utils.LogUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.reactivestreams.Publisher;
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.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.util.annotation.NonNull;

import java.nio.charset.StandardCharsets;

@Component
@Slf4j
public class GatewayGlobalFilter implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        long startTime = System.currentTimeMillis();
        ServerHttpRequest request = exchange.getRequest();
        log.info("请求方法:{},请求路径:{}", request.getMethodValue(), request.getPath().value());
        String mdcIds = request.getHeaders().getFirst(LogUtil.TRACE_ID);
        if (StringUtils.isBlank(mdcIds)) {
            LogUtil.setTraceId();
        } else {
            LogUtil.setTraceId(mdcIds);
        }

        ServerHttpResponseDecorator responseDecorator = new ServerHttpResponseDecorator(exchange.getResponse()) {
            @NonNull
            @Override
            public Mono<Void> writeWith(@NonNull Publisher<? extends DataBuffer> body) {
                if (!(body instanceof Flux)) {
                    return super.writeWith(body);
                }
                @SuppressWarnings("unchecked")
                Flux<? extends DataBuffer> fluxDataBuffer = (Flux<? extends DataBuffer>) body;
                return super.writeWith(fluxDataBuffer.buffer()
                    .map(dataBuffer -> {
                            ServerHttpResponse response = exchange.getResponse();
                            HttpStatus statusCode = response.getStatusCode();
                            DataBuffer responseBody = response.bufferFactory().join(dataBuffer);
                            if (statusCode == null || statusCode.isError()) {
                                log.error("内部服务异常响应:{}", StandardCharsets.UTF_8.decode(responseBody.asByteBuffer()));
                                ResponseEnum responseEnum = (statusCode != null && statusCode.is4xxClientError())
                                    ? ResponseEnum.NOT_FOUND_PATH : ResponseEnum.ERROR;
                                responseBody = response.bufferFactory().wrap(JSON.toJSONBytes(ServerResponseEntity.fail(responseEnum), JSONWriter.Feature.WriteMapNullValue));
                                response.setStatusCode(HttpStatus.OK);
                                response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
                            }
                            log.info("响应:{}", StandardCharsets.UTF_8.decode(responseBody.asByteBuffer()));
                            return responseBody;
                        }
                    )
                );
            }
        };

        return chain.filter(exchange.mutate().response(responseDecorator).build())
            .then(Mono.just(exchange).map(ex -> {
                // 其他操作
                return ex;
            }))
            .then(Mono.fromRunnable(() -> {
                log.info("请求耗时:{}", System.currentTimeMillis() - startTime);
            }));
    }

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

Spring Cloud Getaway 3中不能使用@Order定义过滤器顺序必须实现Ordered接口并且getOrder()必须返回小于0的值否则不生效

  • 为响应数据加密
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import lombok.extern.slf4j.Slf4j;
import org.reactivestreams.Publisher;
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.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.util.annotation.NonNull;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Map;

/**
 * 响应 Body 加密 过滤器
 *
 * @author xuxiaowei
 * @see ServerHttpResponseDecorator
 * @since 0.0.1
 */
@Slf4j
@Component
public class BodyEncryptionGlobalFilter implements GlobalFilter, Ordered {

    /**
     * 加密 过滤器 优先级
     * 

* 响应数据过滤器优先级需要小于 0,否则将无效 */ @Override public int getOrder() { return -1; } @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { ObjectMapper objectMapper = new ObjectMapper(); objectMapper.registerModule(new JavaTimeModule()); ServerHttpResponseDecorator decorator = new ServerHttpResponseDecorator(exchange.getResponse()) { @NonNull @Override public Mono<Void> writeWith(@NonNull Publisher<? extends DataBuffer> body) { @SuppressWarnings("unchecked") Flux<? extends DataBuffer> fluxDataBuffer = (Flux<? extends DataBuffer>) body; return super.writeWith(fluxDataBuffer.buffer() .map(dataBuffer -> { DataBuffer join = exchange.getResponse().bufferFactory().join(dataBuffer); byte[] bytes = new byte[join.readableByteCount()]; join.read(bytes); DataBufferUtils.release(join); log.debug("加密前 body:{}", new String(bytes)); byte[] encryption; try { @SuppressWarnings("unchecked") Map<String, Object> map = objectMapper.readValue(bytes, Map.class); map.put("test", "数据已加密(仅演示,加密方式,自己实现)"); encryption = objectMapper.writeValueAsBytes(map); } catch (IOException e) { encryption = bytes; log.error("数据类型不是 JSON,不加密", e); } log.debug("加密后 body:{}", new String(encryption, StandardCharsets.UTF_8)); return exchange.getResponse().bufferFactory().wrap(encryption); }) ); } }; return chain.filter(exchange.mutate().response(decorator).build()); } }

参考代码

  • 仅读取响应数据
@Component
public class LogRespFilter implements GlobalFilter, Ordered {
 
 	@Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpResponse originalResponse = exchange.getResponse();
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) {
            @Override
            public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
            	//修改header
                HttpHeaders httpHeaders = originalResponse.getHeaders();
                httpHeaders.add("xxxxxx","aaaaaa");
                //输出返回结果
                if (body instanceof Flux) {
                    Mono<Void> newMono = super.writeWith(
                            DataBufferUtils.join(body)
                                    .doOnNext(dataBuffer -> {
                                        String respBody = dataBuffer.toString(StandardCharsets.UTF_8);
                                        //输出body
                                        logger.info("fgwResponse : body = {}", respBody);
                                    })
                    );
                    //输出response,不包含body
                    logger.info("fgwResponse : resp = {}",  JSON.toJSONString(exchange.getResponse()));
                    return newMono;
 
				}
				return super.writeWith(body);
            }
        };
        return chain.filter(exchange.mutate().response(decoratedResponse).build());
    }
}

参考文章

你可能感兴趣的:(微服务,java,spring,spring,boot,gateway,spring,cloud)