Move URL transform methods from ServerHttpResponse to ServerWebExchange
This commit moves `encodeUrl` and `registerUrlEncoder` from ServerHttpResponse to ServerWebExchange. It also renames `encodeUrl` to `transformUrl` and `registerUrlEncoder` to `addUrlTransformer` to make it clearer that these methods do not perform actual URL encodings (i.e. they do not replaceinvalid characters). The `add` prefix (instead of `register`) makes it clearer that each function is added in addition to the previous one. Issue: SPR-15924
This commit is contained in:
parent
5d4ee09d53
commit
02a2c400c7
|
|
@ -19,7 +19,6 @@ package org.springframework.http.server.reactive;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
|
@ -69,9 +68,6 @@ public abstract class AbstractServerHttpResponse implements ServerHttpResponse {
|
|||
|
||||
private final MultiValueMap<String, ResponseCookie> cookies;
|
||||
|
||||
@Nullable
|
||||
private Function<String, String> urlEncoder = url -> url;
|
||||
|
||||
private final AtomicReference<State> state = new AtomicReference<>(State.NEW);
|
||||
|
||||
private final List<Supplier<? extends Mono<Void>>> commitActions = new ArrayList<>(4);
|
||||
|
|
@ -136,16 +132,6 @@ public abstract class AbstractServerHttpResponse implements ServerHttpResponse {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encodeUrl(String url) {
|
||||
return (this.urlEncoder != null ? this.urlEncoder.apply(url) : url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerUrlEncoder(Function<String, String> encoder) {
|
||||
this.urlEncoder = (this.urlEncoder != null ? this.urlEncoder.andThen(encoder) : encoder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeCommit(Supplier<? extends Mono<Void>> action) {
|
||||
this.commitActions.add(action);
|
||||
|
|
|
|||
|
|
@ -16,8 +16,6 @@
|
|||
|
||||
package org.springframework.http.server.reactive;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ReactiveHttpOutputMessage;
|
||||
import org.springframework.http.ResponseCookie;
|
||||
|
|
@ -59,22 +57,4 @@ public interface ServerHttpResponse extends ReactiveHttpOutputMessage {
|
|||
*/
|
||||
void addCookie(ResponseCookie cookie);
|
||||
|
||||
/**
|
||||
* A mechanism for URL rewriting that applications and libraries such as
|
||||
* HTML template libraries to use consistently for all URLs emitted by
|
||||
* the application. Doing so enables the registration of URL encoders via
|
||||
* {@link #registerUrlEncoder} that can insert an id for authentication,
|
||||
* a nonce for CSRF protection, or other.
|
||||
* @param url the URL to encode
|
||||
* @return the encoded URL or the same
|
||||
*/
|
||||
String encodeUrl(String url);
|
||||
|
||||
/**
|
||||
* Register a URL rewriting function for use with {@link #encodeUrl}.
|
||||
* The function must return an encoded URL or the same URL.
|
||||
* @param encoder a URL encoding function to use
|
||||
*/
|
||||
void registerUrlEncoder(Function<String, String> encoder);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@
|
|||
*/
|
||||
package org.springframework.http.server.reactive;
|
||||
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
|
|
@ -80,16 +79,6 @@ public class ServerHttpResponseDecorator implements ServerHttpResponse {
|
|||
getDelegate().addCookie(cookie);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encodeUrl(String url) {
|
||||
return getDelegate().encodeUrl(url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerUrlEncoder(Function<String, String> encoder) {
|
||||
getDelegate().registerUrlEncoder(encoder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataBufferFactory bufferFactory() {
|
||||
return getDelegate().bufferFactory();
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import java.security.Principal;
|
|||
import java.time.Instant;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
|
|
@ -177,6 +178,23 @@ public interface ServerWebExchange {
|
|||
*/
|
||||
boolean checkNotModified(@Nullable String etag, Instant lastModified);
|
||||
|
||||
/**
|
||||
* Transform the given url according to the registered transformation function(s).
|
||||
* By default, this method returns the given {@code url}, though additional
|
||||
* transformation functions can by registered with {@link #addUrlTransformer}
|
||||
* @param url the URL to transform
|
||||
* @return the transformed URL
|
||||
*/
|
||||
String transformUrl(String url);
|
||||
|
||||
/**
|
||||
* Register an additional URL transformation function for use with {@link #transformUrl}.
|
||||
* The given function can be used to insert an id for authentication, a nonce for CSRF
|
||||
* protection, etc.
|
||||
* <p>Note that the given function is applied after any previously registered functions.
|
||||
* @param transformer a URL transformation function to add
|
||||
*/
|
||||
void addUrlTransformer(Function<String, String> transformer);
|
||||
|
||||
/**
|
||||
* Return a builder to mutate properties of this exchange by wrapping it
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2017 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.
|
||||
|
|
@ -18,6 +18,7 @@ package org.springframework.web.server;
|
|||
import java.security.Principal;
|
||||
import java.time.Instant;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
|
|
@ -120,6 +121,15 @@ public class ServerWebExchangeDecorator implements ServerWebExchange {
|
|||
return getDelegate().checkNotModified(etag, lastModified);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String transformUrl(String url) {
|
||||
return getDelegate().transformUrl(url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addUrlTransformer(Function<String, String> transformer) {
|
||||
getDelegate().addUrlTransformer(transformer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import java.util.Collections;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Function;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
|
|
@ -91,6 +92,8 @@ public class DefaultServerWebExchange implements ServerWebExchange {
|
|||
|
||||
private volatile boolean notModified;
|
||||
|
||||
private Function<String, String> urlTransformer = url -> url;
|
||||
|
||||
|
||||
public DefaultServerWebExchange(ServerHttpRequest request, ServerHttpResponse response,
|
||||
WebSessionManager sessionManager, ServerCodecConfigurer codecConfigurer, LocaleContextResolver localeContextResolver) {
|
||||
|
|
@ -322,4 +325,16 @@ public class DefaultServerWebExchange implements ServerWebExchange {
|
|||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String transformUrl(String url) {
|
||||
return this.urlTransformer.apply(url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addUrlTransformer(Function<String, String> transformer) {
|
||||
Assert.notNull(transformer, "'encoder' must not be null");
|
||||
this.urlTransformer = this.urlTransformer.andThen(transformer);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2017 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.
|
||||
|
|
@ -32,9 +32,7 @@ import org.springframework.core.io.buffer.DefaultDataBufferFactory;
|
|||
import org.springframework.http.ResponseCookie;
|
||||
|
||||
import static junit.framework.TestCase.assertTrue;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* @author Rossen Stoyanchev
|
||||
|
|
@ -42,26 +40,6 @@ import static org.junit.Assert.assertSame;
|
|||
*/
|
||||
public class ServerHttpResponseTests {
|
||||
|
||||
@Test
|
||||
public void encodeUrlDefault() throws Exception {
|
||||
TestServerHttpResponse response = new TestServerHttpResponse();
|
||||
assertEquals("/foo", response.encodeUrl("/foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeUrlWithEncoder() throws Exception {
|
||||
TestServerHttpResponse response = new TestServerHttpResponse();
|
||||
response.registerUrlEncoder(s -> s + "?nonce=123");
|
||||
assertEquals("/foo?nonce=123", response.encodeUrl("/foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeUrlWithMultipleEncoders() throws Exception {
|
||||
TestServerHttpResponse response = new TestServerHttpResponse();
|
||||
response.registerUrlEncoder(s -> s + ";p=abc");
|
||||
response.registerUrlEncoder(s -> s + "?q=123");
|
||||
assertEquals("/foo;p=abc?q=123", response.encodeUrl("/foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void writeWith() throws Exception {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Copyright 2002-2017 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
|
||||
*
|
||||
* http://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.server.adapter;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.mock.http.server.reactive.test.MockServerHttpRequest;
|
||||
import org.springframework.mock.http.server.reactive.test.MockServerWebExchange;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public class ServerWebExchangeTests {
|
||||
|
||||
private ServerWebExchange exchange;
|
||||
|
||||
@Before
|
||||
public void createExchange() {
|
||||
MockServerHttpRequest request = MockServerHttpRequest.get("http://example.com").build();
|
||||
this.exchange = new MockServerWebExchange(request);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void transformUrlDefault() throws Exception {
|
||||
assertEquals("/foo", this.exchange.transformUrl("/foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void transformUrlWithEncoder() throws Exception {
|
||||
this.exchange.addUrlTransformer(s -> s + "?nonce=123");
|
||||
assertEquals("/foo?nonce=123", this.exchange.transformUrl("/foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void transformUrlWithMultipleEncoders() throws Exception {
|
||||
this.exchange.addUrlTransformer(s -> s + ";p=abc");
|
||||
this.exchange.addUrlTransformer(s -> s + "?q=123");
|
||||
assertEquals("/foo;p=abc?q=123", this.exchange.transformUrl("/foo"));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -289,9 +289,9 @@ public class RedirectView extends AbstractUrlBasedView {
|
|||
* @param exchange current exchange
|
||||
*/
|
||||
protected Mono<Void> sendRedirect(String targetUrl, ServerWebExchange exchange) {
|
||||
String transformedUrl = (isRemoteHost(targetUrl) ? targetUrl : exchange.transformUrl(targetUrl));
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
String encodedURL = (isRemoteHost(targetUrl) ? targetUrl : response.encodeUrl(targetUrl));
|
||||
response.getHeaders().setLocation(URI.create(encodedURL));
|
||||
response.getHeaders().setLocation(URI.create(transformedUrl));
|
||||
response.setStatusCode(getStatusCode());
|
||||
return Mono.empty();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -203,7 +203,7 @@ public class RequestContext {
|
|||
*/
|
||||
public String getContextUrl(String relativeUrl) {
|
||||
String url = StringUtils.applyRelativePath(getContextPath() + "/", relativeUrl);
|
||||
return getExchange().getResponse().encodeUrl(url);
|
||||
return getExchange().transformUrl(url);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -220,7 +220,7 @@ public class RequestContext {
|
|||
String url = StringUtils.applyRelativePath(getContextPath() + "/", relativeUrl);
|
||||
UriTemplate template = new UriTemplate(url);
|
||||
url = template.expand(params).toASCIIString();
|
||||
return getExchange().getResponse().encodeUrl(url);
|
||||
return getExchange().transformUrl(url);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Reference in New Issue