From ed650891cad795cbabb078d6ae023a9da18105fa Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Tue, 16 Apr 2019 10:06:53 -0400 Subject: [PATCH] Add filter to add exchange to Reactor Context Closes gh-21746 --- .../ServerWebExchangeContextFilter.java | 64 +++++++++++++++++ .../ServerWebExchangeContextFilterTests.java | 71 +++++++++++++++++++ 2 files changed, 135 insertions(+) create mode 100644 spring-web/src/main/java/org/springframework/web/filter/reactive/ServerWebExchangeContextFilter.java create mode 100644 spring-web/src/test/java/org/springframework/web/filter/reactive/ServerWebExchangeContextFilterTests.java diff --git a/spring-web/src/main/java/org/springframework/web/filter/reactive/ServerWebExchangeContextFilter.java b/spring-web/src/main/java/org/springframework/web/filter/reactive/ServerWebExchangeContextFilter.java new file mode 100644 index 0000000000..db376c7019 --- /dev/null +++ b/spring-web/src/main/java/org/springframework/web/filter/reactive/ServerWebExchangeContextFilter.java @@ -0,0 +1,64 @@ +/* + * Copyright 2002-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.web.filter.reactive; + +import java.util.Optional; + +import reactor.core.publisher.Mono; +import reactor.util.context.Context; + +import org.springframework.web.server.ServerWebExchange; +import org.springframework.web.server.WebFilter; +import org.springframework.web.server.WebFilterChain; + +/** + * Inserts an attribute in the Reactor {@link Context} that makes the current + * {@link ServerWebExchange} available under the attribute name + * {@link #EXCHANGE_CONTEXT_ATTRIBUTE}. This is useful for access to the + * exchange without explicitly passing it to components that participate in + * request processing. + * + *

The convenience method {@link #get(Context)} looks up the exchange. + * + * @author Rossen Stoyanchev + * @since 5.2 + */ +public class ServerWebExchangeContextFilter implements WebFilter { + + /** Attribute name under which the exchange is saved in the context. */ + public static final String EXCHANGE_CONTEXT_ATTRIBUTE = + ServerWebExchangeContextFilter.class.getName() + ".EXCHANGE_CONTEXT"; + + + @Override + public Mono filter(ServerWebExchange exchange, WebFilterChain chain) { + return chain.filter(exchange) + .subscriberContext(cxt -> cxt.put(EXCHANGE_CONTEXT_ATTRIBUTE, exchange)); + } + + + /** + * Access the {@link ServerWebExchange} from the Reactor Context, if available, + * which is if {@link ServerWebExchangeContextFilter} is configured for use + * and the give context was obtained from a request processing chain. + * @param context the context in which to access the exchange + * @return the exchange + */ + public static Optional get(Context context) { + return context.getOrEmpty(EXCHANGE_CONTEXT_ATTRIBUTE); + } + +} diff --git a/spring-web/src/test/java/org/springframework/web/filter/reactive/ServerWebExchangeContextFilterTests.java b/spring-web/src/test/java/org/springframework/web/filter/reactive/ServerWebExchangeContextFilterTests.java new file mode 100644 index 0000000000..a804e9d4e4 --- /dev/null +++ b/spring-web/src/test/java/org/springframework/web/filter/reactive/ServerWebExchangeContextFilterTests.java @@ -0,0 +1,71 @@ +/* + * Copyright 2002-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.web.filter.reactive; + +import java.time.Duration; +import java.util.concurrent.atomic.AtomicReference; + +import org.junit.Test; +import reactor.core.publisher.Mono; + +import org.springframework.http.server.reactive.HttpHandler; +import org.springframework.mock.http.server.reactive.test.MockServerHttpRequest; +import org.springframework.mock.http.server.reactive.test.MockServerHttpResponse; +import org.springframework.web.server.ServerWebExchange; +import org.springframework.web.server.adapter.WebHttpHandlerBuilder; + +import static org.junit.Assert.*; + +/** + * Unit tests for {@link ServerWebExchangeContextFilter}. + * @author Rossen Stoyanchev + */ +public class ServerWebExchangeContextFilterTests { + + @Test + public void extractServerWebExchangeFromContext() { + MyService service = new MyService(); + + HttpHandler httpHandler = WebHttpHandlerBuilder + .webHandler(exchange -> service.service().then()) + .filter(new ServerWebExchangeContextFilter()) + .build(); + + httpHandler.handle(MockServerHttpRequest.get("/path").build(), new MockServerHttpResponse()) + .block(Duration.ofSeconds(5)); + + assertNotNull(service.getExchange()); + } + + + private static class MyService { + + private final AtomicReference exchangeRef = new AtomicReference<>(); + + + public ServerWebExchange getExchange() { + return this.exchangeRef.get(); + } + + public Mono service() { + return Mono.just("result").subscriberContext(context -> { + ServerWebExchangeContextFilter.get(context).ifPresent(exchangeRef::set); + return context; + }); + } + } + +}