在使用Spring Cloud Gateway的過程當中,常常須要獲取request body,好比用來作日誌記錄、簽名驗證、加密解密等等。html
網上的資料,解決方案五花八門。因此就整理了通過驗證且已經在線上使用的兩種方法,都是基於官方源碼進行擴展。java
本文使用的Spring Cloud Gateway版本爲2.1.1.RELEASE。spring
ModifyRequestBodyGatewayFilterFactory
在官方文檔中的介紹以下:json
This filter can be used to modify the request body before it is sent downstream by the Gateway.
也就是用來修改request body的,既然能修改,天然就能獲取到。緩存
一個簡單的配置以下:微信
@Bean public RouteLocator routes(RouteLocatorBuilder builder) { return builder.routes() .route("rewrite_request_body", r -> r.path("/post_json") .filters(f -> f.modifyRequestBody(String.class, String.class, MediaType.APPLICATION_JSON_VALUE, (exchange, s) -> Mono.just(s))) .uri("lb://waiter")) .build(); }
注意modifyRequestBody
的方法,有四個參數app
須要特別注意的是inClass和outClass的類型,若是設置得不對,會報錯。ide
優勢post
缺點ui
ReadBodyPredicateFactory是用來讀取並判斷request body是否匹配的謂詞。只是在官方文檔中都沒有說明,但不影響使用。
在代碼裏有以下注釋:
We can only read the body from the request once, once that happens if we try to read the body again an exception will be thrown. The below if/else caches the body object as a request attribute in the ServerWebExchange so if this filter is run more than once (due to more than one route using it) we do not try to read the request body multiple times
大體意思是咱們只能從request中讀取request body一次,若是讀取過了,再次讀取就會拋錯。下面的代碼把request body看成了request attribute緩存在ServerWebExchange
中,若是這個filter運行了屢次,也無需讀取request body屢次。
一個簡單的配置以下:
@Autowired private LogRequestBodyGatewayFilterFactory logRequestBodyGatewayFilterFactory; @Bean public RouteLocator routes(RouteLocatorBuilder builder) { return builder.routes() .route("rewrite_json", r -> r.path("/post_json") .and() .readBody(String.class, requestBody -> true) .filters(f -> f.filter(logRequestBodyGatewayFilterFactory.apply(new LogRequestBodyGatewayFilterFactory.Config()))) .uri("lb://waiter")) .build(); }
注意readBody
的方法,有兩個參數
須要特別注意的是inClass的類型,若是設置得不對,會報錯。
LogRequestBodyGatewayFilterFactory
是自定義的一個GatewayFilterFactory
,因爲ReadBodyPredicateFactory
會緩存request body到ServerWebExchange
,須要用的地方只須要從ServerWebExchange
中獲取便可。
@Slf4j @Component public class LogRequestBodyGatewayFilterFactory extends AbstractGatewayFilterFactory<LogRequestBodyGatewayFilterFactory.Config> { private static final String CACHE_REQUEST_BODY_OBJECT_KEY = "cachedRequestBodyObject"; public LogRequestBodyGatewayFilterFactory() { super(Config.class); } @Override public GatewayFilter apply(Config config) { return (exchange, chain) -> { String requestBody = exchange.getAttribute(CACHE_REQUEST_BODY_OBJECT_KEY); log.info(requestBody); return chain.filter(exchange); }; } public static class Config { } }
優勢
官方文檔中說這兩種方式都沒法經過配置文件進行處理,這樣不是很靈活,很難知足實際的須要。
擴展起來其實也不是很難,只要把沒法經過配置文件進行配置的內容屏蔽掉就能夠,對於ModifyRequestBodyGatewayFilterFactory
是RewriteFunction
,對於ReadBodyPredicateFactory
是Predicate
。
有兩種可行的方案
RequestRateLimiterGatewayFilterFactory
中注入keyResolver
。第一種方式在這裏不詳細說明了,本身實現便可。
若是採用第二種方式,下面的配置與上文中ReadBodyPredicateFactory
部分寫的java config是等效的。
spring.cloud.gateway.routes[0].id=rewrite_json spring.cloud.gateway.routes[0].predicates[0]=Path=/post_json spring.cloud.gateway.routes[0].predicates[1].name=ReadBodyPredicateFactory spring.cloud.gateway.routes[0].predicates[1].args.inClass=#{T(String)} spring.cloud.gateway.routes[0].predicates[1].args.predicate=#{@testPredicate} spring.cloud.gateway.routes[0].filters[0].name=LogRequestBody spring.cloud.gateway.routes[0].uri=lb://waiter
須要提供一個bean,也就是配置文件中的testPredicate
@Component public class TestPredicate implements Predicate { @Override public boolean test(Object o) { return true; } }
主要利用了SpEL,注入類型和bean。
目前不管是ModifyRequestBodyGatewayFilterFactory
仍是ReadBodyPredicateFactory
在2.1.1.RELEASE都仍是BETA版本,但在2.2.0.RC1中,這兩個類上關於BETA版本的註釋都已經沒了,放心大膽的用吧。
看到了這裏必定是真愛了,關注微信公衆號【憨憨的春天】第一時間獲取更新。