form/urlencoded support
This commit is contained in:
@@ -1,12 +1,15 @@
|
||||
package org.springframework.cloud.gateway.filter;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
@@ -21,6 +24,7 @@ import io.netty.handler.codec.http.HttpMethod;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.ipc.netty.NettyPipeline;
|
||||
import reactor.ipc.netty.http.client.HttpClient;
|
||||
import reactor.ipc.netty.http.client.HttpClientRequest;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
@@ -53,26 +57,39 @@ public class RoutingFilter implements GlobalFilter, Ordered {
|
||||
final DefaultHttpHeaders httpHeaders = new DefaultHttpHeaders();
|
||||
request.getHeaders().forEach(httpHeaders::set);
|
||||
|
||||
return this.httpClient.request(method, url, req ->
|
||||
req.options(NettyPipeline.SendOptions::flushOnEach)
|
||||
.headers(httpHeaders)
|
||||
.sendHeaders()
|
||||
.send(request.getBody()
|
||||
.map(DataBuffer::asByteBuffer)
|
||||
.map(Unpooled::wrappedBuffer)))
|
||||
.then(res -> {
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
// put headers and status so filters can modify the response
|
||||
final HttpHeaders headers = new HttpHeaders();
|
||||
res.responseHeaders().forEach(entry -> headers.add(entry.getKey(), entry.getValue()));
|
||||
return this.httpClient.request(method, url, req -> {
|
||||
final HttpClientRequest proxyRequest = req.options(NettyPipeline.SendOptions::flushOnEach)
|
||||
.headers(httpHeaders);
|
||||
|
||||
response.getHeaders().putAll(headers);
|
||||
response.setStatusCode(HttpStatus.valueOf(res.status().code()));
|
||||
if (MediaType.APPLICATION_FORM_URLENCODED.includes(request.getHeaders().getContentType())) {
|
||||
return exchange.getFormData()
|
||||
.then(map -> proxyRequest.sendForm(form -> {
|
||||
for (Map.Entry<String, List<String>> entry: map.entrySet()) {
|
||||
for (String value : entry.getValue()) {
|
||||
form.attr(entry.getKey(), value);
|
||||
}
|
||||
}
|
||||
}).then())
|
||||
.then(chain.filter(exchange));
|
||||
}
|
||||
|
||||
// Defer committing the response until all route filters have run
|
||||
// Put client response as ServerWebExchange attribute and write response later WriteResponseFilter
|
||||
exchange.getAttributes().put(CLIENT_RESPONSE_ATTR, res);
|
||||
return Mono.empty();
|
||||
}).then(chain.filter(exchange));
|
||||
return proxyRequest.sendHeaders()
|
||||
.send(request.getBody()
|
||||
.map(DataBuffer::asByteBuffer)
|
||||
.map(Unpooled::wrappedBuffer));
|
||||
}).then(res -> {
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
// put headers and status so filters can modify the response
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
res.responseHeaders().forEach(entry -> headers.add(entry.getKey(), entry.getValue()));
|
||||
|
||||
response.getHeaders().putAll(headers);
|
||||
response.setStatusCode(HttpStatus.valueOf(res.status().code()));
|
||||
|
||||
// Defer committing the response until all route filters have run
|
||||
// Put client response as ServerWebExchange attribute and write response later WriteResponseFilter
|
||||
exchange.getAttributes().put(CLIENT_RESPONSE_ATTR, res);
|
||||
return Mono.empty();
|
||||
}).then(chain.filter(exchange));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,10 @@ import org.springframework.core.annotation.Order;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.web.reactive.function.BodyInserters;
|
||||
import org.springframework.web.reactive.function.client.ClientResponse;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
@@ -45,6 +48,7 @@ import reactor.test.StepVerifier;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(webEnvironment = RANDOM_PORT)
|
||||
@DirtiesContext//(methodMode = DirtiesContext.MethodMode.AFTER_METHOD)
|
||||
@SuppressWarnings("unchecked")
|
||||
public class GatewayIntegrationTests {
|
||||
|
||||
@@ -246,6 +250,28 @@ public class GatewayIntegrationTests {
|
||||
.verify(DURATION);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void formUrlencodedWorks() {
|
||||
LinkedMultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
|
||||
formData.add("foo", "bar");
|
||||
formData.add("baz", "bam");
|
||||
|
||||
Mono<Map> result = webClient.post()
|
||||
.uri("/post")
|
||||
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
|
||||
.exchange(BodyInserters.fromFormData(formData))
|
||||
.then(response -> response.body(toMono(Map.class)));
|
||||
|
||||
StepVerifier.create(result)
|
||||
.consumeNextWith(map -> {
|
||||
Map<String, Object> form = getMap(map, "form");
|
||||
assertThat(form).containsEntry("foo", "bar");
|
||||
assertThat(form).containsEntry("baz", "bam");
|
||||
})
|
||||
.expectComplete()
|
||||
.verify(DURATION);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postWorks() {
|
||||
Mono<Map> result = webClient.post()
|
||||
|
||||
@@ -48,13 +48,6 @@ spring:
|
||||
filters:
|
||||
- AddResponseHeader=X-Request-Foo, Bar
|
||||
|
||||
# =====================================
|
||||
- id: complex_content_type_test
|
||||
uri: http://httpbin.org:80
|
||||
predicates:
|
||||
- Host=**.complexcontenttype.org
|
||||
- Url=/headers
|
||||
|
||||
# =====================================
|
||||
- id: hystrix_failure_test
|
||||
uri: http://httpbin.org:80
|
||||
@@ -181,8 +174,8 @@ myservice:
|
||||
logging:
|
||||
level:
|
||||
org.springframework.cloud.gateway: TRACE
|
||||
# org.springframework.http.server.reactive: DEBUG
|
||||
# reactor.ipc.netty: DEBUG
|
||||
org.springframework.http.server.reactive: DEBUG
|
||||
reactor.ipc.netty: DEBUG
|
||||
|
||||
management:
|
||||
context-path: /admin
|
||||
|
||||
Reference in New Issue
Block a user