赞
踩
例行每半年一次的工作轮换,接手了同事的网关服务
年底了工作不是很忙,看了下前人的代码,虽然都能读懂,但感觉应该可以再优雅一点
于是把网关的相关知识又翻阅了一下
官方资料
PS:这里如果按新方案调整的话,在结构上会看起来更清晰、可读性上会得到一定的提高
但学习研究是一回事,我肯定不会去直接修改前人的代码,我们还是要对运行稳定的项目持一点敬畏心,搞得不好,手一抖就是一个BUG
// 这里的 exchange 是 过滤器入参 ServerWebExchange
ServerRequest sr = ServerRequest.create(exchange, HandlerStrategies.withDefaults().messageReaders());
// 在 flatMap 方法里做请求体的修改
Mono modifiedBody = sr.bodyToMono(String.class).flatMap
@Override @SuppressWarnings(value = "unchecked") public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) { if (body instanceof Flux) { Flux<DataBuffer> fluxBody = (Flux<DataBuffer>) body; return super.writeWith(fluxBody.buffer().map(dataBuffers -> { DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory(); DataBuffer join = dataBufferFactory.join(dataBuffers); byte[] content = new byte[join.readableByteCount()]; join.read(content); // 释放掉内存 DataBufferUtils.release(join); String originalResponseBody = new String(content, StandardCharsets.UTF_8); // 修改响应体 String updatedResponseBody = modifyBody(originalResponseBody); getDelegate().getHeaders().setContentLength(updatedResponseBody.getBytes().length); return bufferFactory().wrap(updatedResponseBody.getBytes()); })); } return super.writeWith(body); }
ResponseDecorator decorator = new ResponseDecorator(exchange.getResponse());
Mono<Void> filter = chain.filter(exchange.mutate().response(decorator).build());
return filter;
import lombok.extern.slf4j.Slf4j; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.cloud.gateway.filter.factory.rewrite.ModifyRequestBodyGatewayFilterFactory; import org.springframework.core.Ordered; import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; import javax.annotation.Resource; import java.util.Objects; /** * 修改客户端请求体 * @author weiheng * @date 2024-02-01 **/ @Component @Slf4j public class RequestModifyFilter implements GlobalFilter, Ordered { @Resource private ModifyRequestBodyGatewayFilterFactory modifyRequestBodyFilter; @Resource private RequestRewriteFunction requestRewriteFunction; @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpRequest request = exchange.getRequest(); MediaType mediaType = request.getHeaders().getContentType(); String method = request.getMethodValue(); if (Objects.equals(HttpMethod.POST.name(), method) && MediaType.APPLICATION_JSON.isCompatibleWith(mediaType)) { // 如果是POST请求,并且请求体是 application/json 格式 return modifyRequestBodyFilter.apply( new ModifyRequestBodyGatewayFilterFactory.Config() .setRewriteFunction(byte[].class, byte[].class, requestRewriteFunction)) .filter(exchange, chain); } else { return filter(exchange, chain); } } @Override public int getOrder() { return FilterPriorityConstant.REQUEST_BODY_DECRYPT_AND_SIGNATURE_VERIFICATION; } }
import cn.hutool.core.util.RandomUtil; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import lombok.extern.slf4j.Slf4j; import org.reactivestreams.Publisher; import org.springframework.cloud.gateway.filter.factory.rewrite.RewriteFunction; import org.springframework.http.HttpHeaders; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; /** * 修改请求体 * @author weiheng * @date 2024-01-31 **/ @Slf4j @Component public class RequestRewriteFunction implements RewriteFunction<byte[], byte[]> { /** * Applies this function to the given arguments. * * @param serverWebExchange the first function argument * @param bytes the second function argument * @return the function result */ @Override public Publisher<byte[]> apply(ServerWebExchange serverWebExchange, byte[] bytes) { ServerHttpRequest request = serverWebExchange.getRequest(); HttpHeaders headers = request.getHeaders(); String requestId = headers.getFirst(HttpConstant.HEADER_REQUEST_ID); JSONObject requestBody = JSON.parseObject(bytes); log.info(">>>>> requestId[{}], 原始请求体[{}]", requestId, requestBody); try { // TODO 写自己的请求拦截业务,比如请求体的 【解密、验签】 // 测试,看业务服务的 controller类请求体中是否添加了该属性 - 自测OK requestBody.put("traceId", RandomUtil.randomInt()); log.info(">>>>> requestId[{}], 修改后请求体[{}]", requestId, requestBody); return Mono.just(requestBody.toString().getBytes()); } catch (Exception e) { log.error(">>>>> requestId[{}], 修改请求体出错", requestId, e); throw e; } } }
import lombok.extern.slf4j.Slf4j; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.cloud.gateway.filter.factory.rewrite.ModifyResponseBodyGatewayFilterFactory; import org.springframework.core.Ordered; import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; import javax.annotation.Resource; import java.util.Objects; /** * 修改服务端响应体 * @author weiheng * @date 2024-02-01 **/ @Component @Slf4j public class ResponseModifyFilter implements GlobalFilter, Ordered { @Resource private ModifyResponseBodyGatewayFilterFactory modifyResponseBodyFilter; @Resource private ResponseRewriteFunction responseRewriteFunction; @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpRequest request = exchange.getRequest(); MediaType mediaType = request.getHeaders().getContentType(); String method = request.getMethodValue(); if (Objects.equals(HttpMethod.POST.name(), method) && MediaType.APPLICATION_JSON.isCompatibleWith(mediaType)) { // 如果是POST请求,并且请求体是 application/json 格式 return modifyResponseBodyFilter.apply( new ModifyResponseBodyGatewayFilterFactory.Config() .setRewriteFunction(byte[].class, byte[].class, responseRewriteFunction)) .filter(exchange, chain); } else { return filter(exchange, chain); } } @Override public int getOrder() { return FilterPriorityConstant.RESPONSE_BODY_ENCRYPT_AND_ADD_SIGNATURE; } }
import cn.hutool.core.util.RandomUtil; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import lombok.extern.slf4j.Slf4j; import org.reactivestreams.Publisher; import org.springframework.cloud.gateway.filter.factory.rewrite.RewriteFunction; import org.springframework.http.HttpHeaders; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; /** * 修改响应体 * @author weiheng * @date 2024-01-31 **/ @Slf4j @Component public class ResponseRewriteFunction implements RewriteFunction<byte[], byte[]> { /** * Applies this function to the given arguments. * * @param serverWebExchange the first function argument * @param bytes the second function argument * @return the function result */ @Override public Publisher<byte[]> apply(ServerWebExchange serverWebExchange, byte[] bytes) { ServerHttpRequest request = serverWebExchange.getRequest(); HttpHeaders headers = request.getHeaders(); String requestId = headers.getFirst(HttpConstant.HEADER_REQUEST_ID); JSONObject responseBody = JSON.parseObject(bytes); log.info(">>>>> requestId[{}], 原始响应体[{}]", requestId, responseBody); try { // TODO 在这里写自己的业务逻辑,比如响应体的 加密、加签 // 测试,看客户端拿到的响应体中,是否有该属性 - 亲测OK responseBody.put("traceResponse", RandomUtil.randomInt(1000, 9999)); log.info(">>>>> requestId[{}], 修改后的响应体[{}]", requestId, responseBody); return Mono.just(responseBody.toString().getBytes()); } catch (Exception e) { log.error(">>>>> requestId[{}], 修改响应体出错", requestId, e); throw e; } } }
自定义 FilterPriorityConstant 常量类
PS:项目中有好几个过滤器,原先都是在各自的类中定义的优先级,这里统一放到常量里进行管理
这里并未展示真实项目中的所有过滤器,仅作示例
import org.springframework.cloud.gateway.filter.NettyWriteResponseFilter; /** * 过滤器优先级定义 * @author weiheng * @date 2024-01-31 **/ public class FilterPriorityConstant { /** * Useful constant for the highest precedence value. * @see java.lang.Integer#MIN_VALUE */ public static int HIGHEST_PRECEDENCE = Integer.MIN_VALUE; /** * Useful constant for the lowest precedence value. * @see java.lang.Integer#MAX_VALUE */ public static int LOWEST_PRECEDENCE = Integer.MAX_VALUE; /** 商户权限校验 - 根据header的商户ID做接口权限校验 */ public static int MERCHANT_AUTH_VALIDATE = 2; /** 请求体解密验签 */ public static int REQUEST_BODY_DECRYPT_AND_SIGNATURE_VERIFICATION = 3; /** 响应体加密加签 value: -2 */ public static int RESPONSE_BODY_ENCRYPT_AND_ADD_SIGNATURE = NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER - 1; }
以上请求体和响应体的修改,亲测OK
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。