使用场景

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

获取服务响应码,如果异常就修改响应体

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 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 writeWith(@NonNull Publisher body) {

if (!(body instanceof Flux)) {

return super.writeWith(body);

}

@SuppressWarnings("unchecked")

Flux fluxDataBuffer = (Flux) 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 filter(ServerWebExchange exchange, GatewayFilterChain chain) {

ObjectMapper objectMapper = new ObjectMapper();

objectMapper.registerModule(new JavaTimeModule());

ServerHttpResponseDecorator decorator = new ServerHttpResponseDecorator(exchange.getResponse()) {

@NonNull

@Override

public Mono writeWith(@NonNull Publisher body) {

@SuppressWarnings("unchecked")

Flux fluxDataBuffer = (Flux) 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 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 filter(ServerWebExchange exchange, GatewayFilterChain chain) {

ServerHttpResponse originalResponse = exchange.getResponse();

ServerHttpRequest request = exchange.getRequest();

ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) {

@Override

public Mono writeWith(Publisher body) {

//修改header

HttpHeaders httpHeaders = originalResponse.getHeaders();

httpHeaders.add("xxxxxx","aaaaaa");

//输出返回结果

if (body instanceof Flux) {

Mono 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());

}

}

参考文章

文章来源

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: