Spring Cloud Gateway(Greenwich)如何在过滤器中读取请求体request body。

Spring Cloud Gateway(Greenwich)如何在过滤器中读取请求体request body。

Spring Cloud Gateway(Greenwich)如何在过滤器中读取请求体request body。

因为需要在网关层做一些验证操作,需要访问请求体的参数。我使用的Spring Cloud版本是Greenwich,在网上找了很多资料之后,经过验证找到了简单的解决方案。

Gateway内置过滤器工厂ReadBodyPredicateFactory

ReadBodyPredicateFactory对请求体参数做了缓存,如下代码:

protected static final Log LOGGER = LogFactory.getLog(ReadBodyPredicateFactory.class);

private static final String TEST_ATTRIBUTE = "read_body_predicate_test_attribute";
private static final String CACHE_REQUEST_BODY_OBJECT_KEY = "cachedRequestBodyObject";
private static final List<HttpMessageReader<?>> messageReaders = HandlerStrategies.withDefaults().messageReaders();

public ReadBodyPredicateFactory() {
	super(Config.class);
}

@Override
@SuppressWarnings("unchecked")
public AsyncPredicate<ServerWebExchange> applyAsync(Config config) {
	return exchange -> {
		Class inClass = config.getInClass();

		Object cachedBody = exchange.getAttribute(CACHE_REQUEST_BODY_OBJECT_KEY);
		Mono<?> modifiedBody;
		if (cachedBody != null) {
			try {
				boolean test = config.predicate.test(cachedBody);
				exchange.getAttributes().put(TEST_ATTRIBUTE, test);
				return Mono.just(test);
			} catch (ClassCastException e) {
				if (LOGGER.isDebugEnabled()) {
					LOGGER.debug("Predicate test failed because class in predicate does not match the cached body object",
							e);
				}
			}
			return Mono.just(false);
		} else {
			return DataBufferUtils.join(exchange.getRequest().getBody())
					.flatMap(dataBuffer -> {
						DataBufferUtils.retain(dataBuffer);
						Flux<DataBuffer> cachedFlux = Flux.defer(() -> Flux.just(dataBuffer.slice(0, dataBuffer.readableByteCount())));

						ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator(exchange.getRequest()) {
							@Override
							public Flux<DataBuffer> getBody() {
								return cachedFlux;
							}
						};
						return ServerRequest.create(exchange.mutate().request(mutatedRequest).build(), messageReaders)
								.bodyToMono(inClass)
								.doOnNext(objectValue -> {
									exchange.getAttributes().put(CACHE_REQUEST_BODY_OBJECT_KEY, objectValue);
									exchange.getAttributes().put(CACHED_REQUEST_BODY_KEY, cachedFlux);
								})
								.map(objectValue -> config.predicate.test(objectValue));
					});

		}
	};
}

如上所示,ReadBodyPredicateFactory将请求体类容缓存到了exchange中,所以只需要在后续过滤器(我是用的全局过滤器,全局过滤器执行顺序在所有过滤器之后)中,使用

Object cachedBody = exchange.getAttribute(CACHE_REQUEST_BODY_OBJECT_KEY);

即可获取request body的参数。在此之前还需要配置一下路由器,如下。

路由器配置

这里没有找到使用yml配置ReadBodyPredicateFactory的方法,所以使用@Bean的方式配置,代码如下:

@Bean
public RouteLocator myRoutes(RouteLocatorBuilder builder) {

	/*
	route1 是post请求,Content-Type是application/x-www-form-urlencoded,readbody为String.class
	route2 是post请求,Content-Type是application/json,readbody为Object.class
	 */
	RouteLocatorBuilder.Builder routes = builder.routes();
	RouteLocatorBuilder.Builder serviceProvider = routes
			.route("PUSH-SERVER",
					r -> r
							.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE)
							.and()
							.method(HttpMethod.POST)
							.and()
							.readBody(String.class, readBody -> {
								return true;
							})
							.and()
							.path("/gateway/push-server/**")
							.filters(f -> {
								f.stripPrefix(2);
								return f;
							})
							.uri("lb://PUSH-SERVER"))
			.route("PUSH-SERVER",
					r -> r
							.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
							.and()
							.method(HttpMethod.POST)
							.and()
							.readBody(String.class, readBody -> {
								return true;
							})
							.and()
							.path("/gateway/push-server/**")
							.filters(f -> {
								f.stripPrefix(2);
								return f;
							})
							.uri("lb://PUSH-SERVER"));
	RouteLocator routeLocator = serviceProvider.build();
	return routeLocator;
}

其中.readBody()方法就是配置上述过滤器工厂,可以在其 {} 做一些日志输入操作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值