Browse Source
This commit adds a `DefaultHttpRequestBuilder` and its companion static builders in `HttpRequestBuilders`. This allows to build client requests with a friendly builder API, inspired by Spring's MockMvc API.pull/1111/head
3 changed files with 278 additions and 1 deletions
@ -0,0 +1,167 @@
@@ -0,0 +1,167 @@
|
||||
/* |
||||
* Copyright 2002-2016 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.client.reactive; |
||||
|
||||
|
||||
import java.net.URI; |
||||
import java.net.URISyntaxException; |
||||
import java.util.ArrayList; |
||||
import java.util.Arrays; |
||||
import java.util.List; |
||||
import java.util.Optional; |
||||
import java.util.stream.Collectors; |
||||
|
||||
import org.reactivestreams.Publisher; |
||||
import reactor.core.publisher.Flux; |
||||
|
||||
import org.springframework.core.ResolvableType; |
||||
import org.springframework.core.codec.Encoder; |
||||
import org.springframework.http.HttpCookie; |
||||
import org.springframework.http.HttpHeaders; |
||||
import org.springframework.http.HttpMethod; |
||||
import org.springframework.http.MediaType; |
||||
import org.springframework.http.client.reactive.ClientHttpRequest; |
||||
import org.springframework.http.client.reactive.ClientHttpRequestFactory; |
||||
import org.springframework.web.client.RestClientException; |
||||
|
||||
/** |
||||
* Builds a {@link ClientHttpRequest} |
||||
* |
||||
* <p>See static factory methods in {@link HttpRequestBuilders} |
||||
* |
||||
* @author Brian Clozel |
||||
* @see HttpRequestBuilders |
||||
*/ |
||||
public class DefaultHttpRequestBuilder implements HttpRequestBuilder { |
||||
|
||||
protected HttpMethod httpMethod; |
||||
|
||||
protected HttpHeaders httpHeaders; |
||||
|
||||
protected URI url; |
||||
|
||||
protected Flux contentPublisher; |
||||
|
||||
protected List<Encoder<?>> messageEncoders; |
||||
|
||||
protected final List<HttpCookie> cookies = new ArrayList<HttpCookie>(); |
||||
|
||||
protected DefaultHttpRequestBuilder() { |
||||
} |
||||
|
||||
public DefaultHttpRequestBuilder(HttpMethod httpMethod, String urlTemplate, Object... urlVariables) throws RestClientException { |
||||
this.httpMethod = httpMethod; |
||||
this.httpHeaders = new HttpHeaders(); |
||||
this.url = parseURI(urlTemplate); |
||||
} |
||||
|
||||
public DefaultHttpRequestBuilder(HttpMethod httpMethod, URI url) { |
||||
this.httpMethod = httpMethod; |
||||
this.httpHeaders = new HttpHeaders(); |
||||
this.url = url; |
||||
} |
||||
|
||||
protected DefaultHttpRequestBuilder setMessageEncoders(List<Encoder<?>> messageEncoders) { |
||||
this.messageEncoders = messageEncoders; |
||||
return this; |
||||
} |
||||
|
||||
private URI parseURI(String uri) throws RestClientException { |
||||
try { |
||||
return new URI(uri); |
||||
} |
||||
catch (URISyntaxException e) { |
||||
throw new RestClientException("could not parse URL template", e); |
||||
} |
||||
} |
||||
|
||||
public DefaultHttpRequestBuilder param(String name, String... values) { |
||||
return this; |
||||
} |
||||
|
||||
public DefaultHttpRequestBuilder header(String name, String... values) { |
||||
Arrays.stream(values).forEach(value -> this.httpHeaders.add(name, value)); |
||||
return this; |
||||
} |
||||
|
||||
public DefaultHttpRequestBuilder headers(HttpHeaders httpHeaders) { |
||||
this.httpHeaders = httpHeaders; |
||||
return this; |
||||
} |
||||
|
||||
public DefaultHttpRequestBuilder contentType(MediaType contentType) { |
||||
this.httpHeaders.setContentType(contentType); |
||||
return this; |
||||
} |
||||
|
||||
public DefaultHttpRequestBuilder contentType(String contentType) { |
||||
this.httpHeaders.setContentType(MediaType.parseMediaType(contentType)); |
||||
return this; |
||||
} |
||||
|
||||
public DefaultHttpRequestBuilder accept(MediaType... mediaTypes) { |
||||
this.httpHeaders.setAccept(Arrays.asList(mediaTypes)); |
||||
return this; |
||||
} |
||||
|
||||
public DefaultHttpRequestBuilder accept(String... mediaTypes) { |
||||
this.httpHeaders.setAccept(Arrays.stream(mediaTypes) |
||||
.map(type -> MediaType.parseMediaType(type)) |
||||
.collect(Collectors.toList())); |
||||
return this; |
||||
} |
||||
|
||||
public DefaultHttpRequestBuilder content(Object content) { |
||||
this.contentPublisher = Flux.just(content); |
||||
return this; |
||||
} |
||||
|
||||
public DefaultHttpRequestBuilder contentStream(Publisher content) { |
||||
this.contentPublisher = Flux.from(content); |
||||
return this; |
||||
} |
||||
|
||||
public ClientHttpRequest build(ClientHttpRequestFactory factory) { |
||||
ClientHttpRequest request = factory.createRequest(this.httpMethod, this.url, this.httpHeaders); |
||||
request.getHeaders().putAll(this.httpHeaders); |
||||
|
||||
if (this.contentPublisher != null) { |
||||
ResolvableType requestBodyType = ResolvableType.forInstance(this.contentPublisher); |
||||
MediaType mediaType = request.getHeaders().getContentType(); |
||||
|
||||
Optional<Encoder<?>> messageEncoder = resolveEncoder(requestBodyType, mediaType); |
||||
|
||||
if (messageEncoder.isPresent()) { |
||||
request.setBody(messageEncoder.get().encode(this.contentPublisher, requestBodyType, mediaType)); |
||||
} |
||||
else { |
||||
// TODO: wrap with client exception?
|
||||
request.setBody(Flux.error(new IllegalStateException("Can't write request body" + |
||||
"of type '" + requestBodyType.toString() + |
||||
"' for content-type '" + mediaType.toString() + "'"))); |
||||
} |
||||
} |
||||
|
||||
return request; |
||||
} |
||||
|
||||
protected Optional<Encoder<?>> resolveEncoder(ResolvableType type, MediaType mediaType) { |
||||
return this.messageEncoders.stream() |
||||
.filter(e -> e.canEncode(type, mediaType)).findFirst(); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,110 @@
@@ -0,0 +1,110 @@
|
||||
/* |
||||
* Copyright 2002-2016 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.client.reactive; |
||||
|
||||
import org.springframework.http.HttpMethod; |
||||
|
||||
/** |
||||
* Static factory methods for {@link DefaultHttpRequestBuilder RequestBuilders}. |
||||
* |
||||
* @author Brian Clozel |
||||
*/ |
||||
public abstract class HttpRequestBuilders { |
||||
|
||||
/** |
||||
* Create a {@link DefaultHttpRequestBuilder} for a GET request. |
||||
* |
||||
* @param urlTemplate a URL template; the resulting URL will be encoded |
||||
* @param urlVariables zero or more URL variables |
||||
*/ |
||||
public static DefaultHttpRequestBuilder get(String urlTemplate, Object... urlVariables) { |
||||
return new DefaultHttpRequestBuilder(HttpMethod.GET, urlTemplate, urlVariables); |
||||
} |
||||
|
||||
/** |
||||
* Create a {@link DefaultHttpRequestBuilder} for a POST request. |
||||
* |
||||
* @param urlTemplate a URL template; the resulting URL will be encoded |
||||
* @param urlVariables zero or more URL variables |
||||
*/ |
||||
public static DefaultHttpRequestBuilder post(String urlTemplate, Object... urlVariables) { |
||||
return new DefaultHttpRequestBuilder(HttpMethod.POST, urlTemplate, urlVariables); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Create a {@link DefaultHttpRequestBuilder} for a PUT request. |
||||
* |
||||
* @param urlTemplate a URL template; the resulting URL will be encoded |
||||
* @param urlVariables zero or more URL variables |
||||
*/ |
||||
public static DefaultHttpRequestBuilder put(String urlTemplate, Object... urlVariables) { |
||||
return new DefaultHttpRequestBuilder(HttpMethod.PUT, urlTemplate, urlVariables); |
||||
} |
||||
|
||||
/** |
||||
* Create a {@link DefaultHttpRequestBuilder} for a PATCH request. |
||||
* |
||||
* @param urlTemplate a URL template; the resulting URL will be encoded |
||||
* @param urlVariables zero or more URL variables |
||||
*/ |
||||
public static DefaultHttpRequestBuilder patch(String urlTemplate, Object... urlVariables) { |
||||
return new DefaultHttpRequestBuilder(HttpMethod.PATCH, urlTemplate, urlVariables); |
||||
} |
||||
|
||||
/** |
||||
* Create a {@link DefaultHttpRequestBuilder} for a DELETE request. |
||||
* |
||||
* @param urlTemplate a URL template; the resulting URL will be encoded |
||||
* @param urlVariables zero or more URL variables |
||||
*/ |
||||
public static DefaultHttpRequestBuilder delete(String urlTemplate, Object... urlVariables) { |
||||
return new DefaultHttpRequestBuilder(HttpMethod.DELETE, urlTemplate, urlVariables); |
||||
} |
||||
|
||||
/** |
||||
* Create a {@link DefaultHttpRequestBuilder} for an OPTIONS request. |
||||
* |
||||
* @param urlTemplate a URL template; the resulting URL will be encoded |
||||
* @param urlVariables zero or more URL variables |
||||
*/ |
||||
public static DefaultHttpRequestBuilder options(String urlTemplate, Object... urlVariables) { |
||||
return new DefaultHttpRequestBuilder(HttpMethod.OPTIONS, urlTemplate, urlVariables); |
||||
} |
||||
|
||||
/** |
||||
* Create a {@link DefaultHttpRequestBuilder} for a HEAD request. |
||||
* |
||||
* @param urlTemplate a URL template; the resulting URL will be encoded |
||||
* @param urlVariables zero or more URL variables |
||||
*/ |
||||
public static DefaultHttpRequestBuilder head(String urlTemplate, Object... urlVariables) { |
||||
return new DefaultHttpRequestBuilder(HttpMethod.HEAD, urlTemplate, urlVariables); |
||||
} |
||||
|
||||
/** |
||||
* Create a {@link DefaultHttpRequestBuilder} for a request with the given HTTP method. |
||||
* |
||||
* @param httpMethod the HTTP method |
||||
* @param urlTemplate a URL template; the resulting URL will be encoded |
||||
* @param urlVariables zero or more URL variables |
||||
*/ |
||||
public static DefaultHttpRequestBuilder request(HttpMethod httpMethod, String urlTemplate, Object... urlVariables) { |
||||
return new DefaultHttpRequestBuilder(httpMethod, urlTemplate, urlVariables); |
||||
} |
||||
|
||||
} |
||||
Loading…
Reference in new issue