import org.springframework.web.reactive.function.BodyInserter;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.ExchangeStrategies;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
public class ModifyResponseBodyGatewayFilterFactory extends AbstractGatewayFilterFactory<ModifyResponseBodyGatewayFilterFactory.Config> {
private final ServerCodecConfigurer codecConfigurer;
public ModifyResponseBodyGatewayFilterFactory(ServerCodecConfigurer codecConfigurer) {
super(ModifyResponseBodyGatewayFilterFactory.Config.class);
this.codecConfigurer = codecConfigurer;
}
public GatewayFilter apply(ModifyResponseBodyGatewayFilterFactory.Config config) {
return new ModifyResponseBodyGatewayFilterFactory.ModifyResponseGatewayFilter(config);
}
public static class Config {
private Class inClass;
private Class outClass;
private Map<String, Object> inHints;
private Map<String, Object> outHints;
private String newContentType;
private RewriteFunction rewriteFunction;
public Config() {
}
public Class getInClass() {
return this.inClass;
}
public ModifyResponseBodyGatewayFilterFactory.Config setInClass(Class inClass) {
this.inClass = inClass;
return this;
}
public Class getOutClass() {
return this.outClass;
}
public ModifyResponseBodyGatewayFilterFactory.Config setOutClass(Class outClass) {
this.outClass = outClass;
return this;
}
public Map<String, Object> getInHints() {
return this.inHints;
}
public ModifyResponseBodyGatewayFilterFactory.Config setInHints(Map<String, Object> inHints) {
this.inHints = inHints;
return this;
}
public Map<String, Object> getOutHints() {
return this.outHints;
}
public ModifyResponseBodyGatewayFilterFactory.Config setOutHints(Map<String, Object> outHints) {
this.outHints = outHints;
return this;
}
public String getNewContentType() {
return this.newContentType;
}
public ModifyResponseBodyGatewayFilterFactory.Config setNewContentType(String newContentType) {
this.newContentType = newContentType;
return this;
}
public RewriteFunction getRewriteFunction() {
return this.rewriteFunction;
}
public <T, R> ModifyResponseBodyGatewayFilterFactory.Config setRewriteFunction(Class inClass, Class outClass, RewriteFunction<T, R> rewriteFunction) {
this.setInClass(inClass);
this.setOutClass(outClass);
this.setRewriteFunction(rewriteFunction);
return this;
}
public ModifyResponseBodyGatewayFilterFactory.Config setRewriteFunction(RewriteFunction rewriteFunction) {
this.rewriteFunction = rewriteFunction;
return this;
}
}
public class ResponseAdapter implements ClientHttpResponse {
private final Flux flux;
private final HttpHeaders headers;
public ResponseAdapter(Publisher<? extends DataBuffer> body, HttpHeaders headers) {
this.headers = headers;
if (body instanceof Flux) {
this.flux = (Flux)body;
} else {
this.flux = ((Mono)body).flux();
}
}
public Flux getBody() {
return this.flux;
}
public HttpHeaders getHeaders() {
return this.headers;
}
public HttpStatus getStatusCode() {
return null;
}
public int getRawStatusCode() {
return 0;
}
public MultiValueMap<String, ResponseCookie> getCookies() {
return null;
}
}
public class ModifyResponseGatewayFilter implements GatewayFilter, Ordered {
private final ModifyResponseBodyGatewayFilterFactory.Config config;
public ModifyResponseGatewayFilter(ModifyResponseBodyGatewayFilterFactory.Config config) {
this.config = config;
}
public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpResponseDecorator responseDecorator = new ServerHttpResponseDecorator(exchange.getResponse()) {
public Mono writeWith(Publisher<? extends DataBuffer> body) {
Class inClass = ModifyResponseGatewayFilter.this.config.getInClass();
Class outClass = ModifyResponseGatewayFilter.this.config.getOutClass();
MediaType originalResponseContentType = (MediaType)exchange.getAttribute(“original_response_content_type”);
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(originalResponseContentType);
ModifyResponseBodyGatewayFilterFactory.ResponseAdapter responseAdapter = ModifyResponseBodyGatewayFilterFactory.this.new ResponseAdapter(body, httpHeaders);
DefaultClientResponse clientResponse = new DefaultClientResponse(responseAdapter, ExchangeStrategies.withDefaults());
Mono modifiedBody = clientResponse.bodyToMono(inClass).flatMap((originalBody) -> {
return ModifyResponseGatewayFilter.this.config.rewriteFunction.apply(exchange, originalBody);
});
BodyInserter bodyInserter = BodyInserters.fromPublisher(modifiedBody, outClass);
CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange, exchange.getResponse().getHeaders());
return bodyInserter.insert(outputMessage, new BodyInserterContext()).then(Mono.defer(() -> {
long contentLength1 = this.getDelegate().getHeaders().getContentLength();
Flux messageBody = outputMessage.getBody();
HttpHeaders headers = this.getDelegate().getHeaders();
if (!headers.containsKey(“Transfer-Encoding”)) {
messageBody = messageBody.doOnNext((data) -> {
headers.setContentLength((long)data.readableByteCount());
});
}
return this.getDelegate().writeWith(messageBody);
}));
}
public Mono writeAndFlushWith(Publisher<? extends Publisher<? extends DataBuffer>> body) {
return this.writeWith(Flux.from(body).flatMapSequential(§ -> {
return p;
}));
}
};
return chain.filter(exchange.mutate().response(responseDecorator).build());
}
public int getOrder() {
return -2;
}
}
}
复制代码
实现代码
====
根据源码示例,可以在网关过滤器添加类似逻辑,实现修改响应数据。
ResponseFilter
@Component
@Slf4j
public class ResponseFilter implements GlobalFilter, Ordered {
@Override
public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
URI uri = request.getURI();
String url = uri.getPath();
HttpStatus statusCode = exchange.getResponse().getStatusCode();
if(Objects.equals(statusCode, HttpStatus.BAD_REQUEST) || Objects.equals(statusCode, HttpStatus.TOO_MANY_REQUESTS)){
// 如果是特殊的请求,已处理响应内容,这里不再处理
return chain.filter(exchange);
}
// 根据具体业务内容,修改响应体
return modifyResponseBody(exchange, chain);
}
/**
-
修改响应体
-
@param exchange
-
@param chain
-
@return
*/
private Mono modifyResponseBody(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpResponseDecorator responseDecorator = new ServerHttpResponseDecorator(exchange.getResponse()) {
public Mono writeWith(Publisher<? extends DataBuffer> body) {
MediaType originalResponseContentType = (MediaType)exchange.getAttribute(“original_response_content_type”);
HttpHeaders httpHeaders = new HttpHeaders();
AtomicBoolean isHttpCodeOK = new AtomicBoolean(true);
httpHeaders.setContentType(originalResponseContentType);
ResponseAdapter responseAdapter = new ResponseAdapter(body, httpHeaders);
HttpStatus statusCode = this.getStatusCode();
// 修改后的响应体
Mono modifiedBody = getModifiedBody(statusCode, isHttpCodeOK, responseAdapter, exchange);
// 业务上的开关,表示是否开启加密,如果开启,就需要修改响应体,将响应体数据加密。开关从上下文获取。这里只关心是一个boolean值即可。
Boolean flag;
BodyInserter bodyInserter;
if(!flag) {
// 不需要修改响应数据
bodyInserter = BodyInserters.fromPublisher(modifiedBody, String.class);
}else {
// 需要修改响应数据
// 这里最后采用了 ByteArrayResource 类去处理
bodyInserter = BodyInserters.fromPublisher(modifiedBody, ByteArrayResource.class);
}
CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange, httpHeaders);
return bodyInserter.insert(outputMessage, new BodyInserterContext()).then(Mono.defer(() -> {
Flux messageBody = outputMessage.getBody();
ServerHttpResponse httpResponse = this.getDelegate();
HttpHeaders headers = httpResponse.getHeaders();
if (!headers.containsKey(“Transfer-Encoding”)) {
messageBody = messageBody.doOnNext((data) -> {
headers.setContentLength((long)data.readableByteCount());
});
}
if(!isHttpCodeOK.get()){
// 业务处理不是200,说明有异常,设置httpCode状态码是500
httpResponse.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
}
return httpResponse.writeWith(messageBody);
}));
}
public Mono writeAndFlushWith(Publisher<? extends Publisher<? extends DataBuffer>> body) {
return this.writeWith(Flux.from(body).flatMapSequential(§ -> {
return p;
}));
}
};
return chain.filter(exchange.mutate().response(responseDecorator).build());
}
public class ResponseAdapter implements ClientHttpResponse {
private final Flux flux;
private final HttpHeaders headers;
public ResponseAdapter(Publisher<? extends DataBuffer> body, HttpHeaders headers) {
this.headers = headers;
if (body instanceof Flux) {
this.flux = (Flux)body;
} else {
this.flux = ((Mono)body).flux();
}
}
public Flux getBody() {
return this.flux;
}
public HttpHeaders getHeaders() {
return this.headers;
}
public HttpStatus getStatusCode() {
return null;
}
public int getRawStatusCode() {
return 0;
}
public MultiValueMap<String, ResponseCookie> getCookies() {
return null;
}
}
@Override
public int getOrder() {
return FilterOrderConstant.getOrder(this.getClass().getName());
}
private Mono getModifiedBody(HttpStatus httpStatus, AtomicBoolean isHttpCodeOK, ResponseAdapter responseAdapter, ServerWebExchange exchange){
switch (httpStatus){
// 业务上需要特殊处理的状态码
case BAD_REQUEST:
case METHOD_NOT_ALLOWED:
isHttpCodeOK.set(false);
return getMono(HttpCode.BAD_REQUEST, exchange);
case INTERNAL_SERVER_ERROR:
isHttpCodeOK.set(false);
return getMono(HttpCode.SERVER_ERROR, exchange);
default:
// 主要处理流程
return getNormalBody(isHttpCodeOK, responseAdapter, exchange);
}
}
private Mono getNormalBody(AtomicBoolean isHttpCodeOK, ResponseAdapter responseAdapter, ServerWebExchange exchange){
DefaultClientResponse clientResponse = new DefaultClientResponse(responseAdapter, ExchangeStrategies.withDefaults());
return clientResponse.bodyToMono(String.class).flatMap((originalBody) -> {
// 业务上的开关,表示是否开启加密,如果开启,就需要修改响应体,将响应体数据加密。开关从上下文获取。这里只关心是一个boolean值即可。
Boolean flag;
ObjectMapper objectMapper = new ObjectMapper();
try {
R r = objectMapper.readValue(originalBody, R.class);
/**
- 异常处理流程
*/
if(!r.getCode().equals(HttpCode.SUCCESS.getCode())){
// 业务处理不是200,说明有异常,直接返回对应错误
isHttpCodeOK.set(false);
ErrorR errorR = new ErrorR()
.setCode(r.getCode())
.setMsg(r.getMsg());
String json = objectMapper.writeValueAsString(errorR);
log.info(“json = {}”, json);
if(!flag) {
// 不需要加密,则不修改响应体
return Mono.just(json);
}else {
// 对返回数据进行加密 EncryptionUtil.encrypt(json, key)
// 具体加密逻辑不再阐述,这里可以理解成string 转成 byte[] 处理
byte[] encrypt = EncryptionUtil.encrypt(“{}”, key);
ByteArrayResource byteArrayResource = new ByteArrayResource(encrypt);
// 修改响应体,使用 byteArrayResource 封装
return Mono.just(byteArrayResource);
}
}
// 业务处理是200,截取data内容
Object data = r.getData();
if(null == data){
// 返回数据如果为空,则返回空对象
if(!flag) {
// 不需要加密,则不修改响应体
return Mono.just(“{}”);
}else {
// 对返回数据进行加密 EncryptionUtil.encrypt(json, key)
// 具体加密逻辑不再阐述,这里可以理解成string 转成 byte[] 处理
byte[] encrypt = EncryptionUtil.encrypt(“{}”, key);
ByteArrayResource byteArrayResource = new ByteArrayResource(encrypt);
return Mono.just(byteArrayResource);
}
}
/**
- 主要处理流程
*/
String json = objectMapper.writeValueAsString(data);
if(!flag) {
// 不需要加密,则不修改响应体
return Mono.just(json);
}else {
// 对返回数据进行加密 EncryptionUtil.encrypt(json, key)
// 具体加密逻辑不再阐述,这里可以理解成string 转成 byte[] 处理