Introduce RequestEntity and builder
This commit introduces the RequestEntity, a class similar to ResponseEntity, but meant for HTTP requests rather than responses. The RequestEntity can be used both in RestTemplate as well as @MVC scenarios. The class also comes with a builder, similar to the one found in ResponseEntity, which allows for building of a RequestEntity through a fluent API. Issue: SPR-11752
This commit is contained in:
parent
b7984f21d8
commit
f6fbdafb6a
|
@ -0,0 +1,597 @@
|
|||
/*
|
||||
* Copyright 2002-2014 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.http;
|
||||
|
||||
import java.net.URI;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.web.util.UriTemplate;
|
||||
|
||||
/**
|
||||
* Extension of {@link HttpEntity} that adds a {@linkplain HttpMethod method} and
|
||||
* {@linkplain URI uri}.
|
||||
* Used in {@code RestTemplate} as well {@code @Controller} methods.
|
||||
*
|
||||
* <p>In {@code RestTemplate}, this class is used as parameter in
|
||||
* {@link org.springframework.web.client.RestTemplate#exchange(RequestEntity, Class) exchange()}:
|
||||
* <pre class="code">
|
||||
* MyRequest body = ...
|
||||
* RequestEntity<MyRequest> request = RequestEntity.post("http://example.com/{foo}", "bar").accept(MediaType.APPLICATION_JSON).body(body);
|
||||
* ResponseEntity<MyResponse> response = template.exchange(request, MyResponse.class);
|
||||
* </pre>
|
||||
*
|
||||
* <p>Can also be used in Spring MVC, as a parameter in a @Controller method:
|
||||
* <pre class="code">
|
||||
* @RequestMapping("/handle")
|
||||
* public void handle(RequestEntity<String> request) {
|
||||
* HttpMethod method = request.getMethod();
|
||||
* URI url = request.getUrl();
|
||||
* String body = request.getBody();
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @since 4.1
|
||||
* @see #getMethod()
|
||||
* @see #getUrl()
|
||||
*/
|
||||
public class RequestEntity<T> extends HttpEntity<T> {
|
||||
|
||||
private final HttpMethod method;
|
||||
|
||||
private final URI url;
|
||||
|
||||
/**
|
||||
* Create a new {@code RequestEntity} with the given method and URL, and no body nor headers.
|
||||
* @param method the method
|
||||
* @param url the URL
|
||||
*/
|
||||
public RequestEntity(HttpMethod method, URI url) {
|
||||
super();
|
||||
this.method = method;
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@code RequestEntity} with the given method, URL, body, and no headers.
|
||||
* @param body the body
|
||||
* @param method the method
|
||||
* @param url the URL
|
||||
*/
|
||||
public RequestEntity(T body, HttpMethod method, URI url) {
|
||||
super(body);
|
||||
this.method = method;
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@code RequestEntity} with the given method, URL, body, headers and no
|
||||
* body
|
||||
* @param headers the headers
|
||||
* @param method the method
|
||||
* @param url the URL
|
||||
*/
|
||||
public RequestEntity(MultiValueMap<String, String> headers, HttpMethod method, URI url) {
|
||||
super(headers);
|
||||
this.method = method;
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@code RequestEntity} with the given method, URL, body, headers and
|
||||
* body
|
||||
* @param body the body
|
||||
* @param headers the headers
|
||||
* @param method the method
|
||||
* @param url the URL
|
||||
*/
|
||||
public RequestEntity(T body, MultiValueMap<String, String> headers,
|
||||
HttpMethod method, URI url) {
|
||||
super(body, headers);
|
||||
this.method = method;
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the HTTP method of the request.
|
||||
* @return the HTTP method as an {@code HttpMethod} enum value
|
||||
*/
|
||||
public HttpMethod getMethod() {
|
||||
return method;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the URL of the request.
|
||||
* @return the URL as a {@code URI}
|
||||
*/
|
||||
public URI getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (!(other instanceof RequestEntity)) {
|
||||
return false;
|
||||
}
|
||||
RequestEntity<?> otherEntity = (RequestEntity<?>) other;
|
||||
return (ObjectUtils.nullSafeEquals(this.method, otherEntity.method) &&
|
||||
ObjectUtils.nullSafeEquals(this.url, otherEntity.url) &&
|
||||
super.equals(other));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 29 * super.hashCode() +
|
||||
29 * ObjectUtils.nullSafeHashCode(this.method) +
|
||||
ObjectUtils.nullSafeHashCode(this.url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder("<");
|
||||
builder.append(this.method.toString());
|
||||
builder.append(' ');
|
||||
builder.append(this.url);
|
||||
builder.append(',');
|
||||
T body = getBody();
|
||||
HttpHeaders headers = getHeaders();
|
||||
if (body != null) {
|
||||
builder.append(body);
|
||||
if (headers != null) {
|
||||
builder.append(',');
|
||||
}
|
||||
}
|
||||
if (headers != null) {
|
||||
builder.append(headers);
|
||||
}
|
||||
builder.append('>');
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
// Static builder methods
|
||||
|
||||
/**
|
||||
* Creates a builder with the given method, url, and uri variables.
|
||||
* <p>URI Template variables are expanded using the given URI variables, if any.
|
||||
* @param method the HTTP method (GET, POST, etc)
|
||||
* @param url the URL
|
||||
* @param uriVariables the variables to expand in the template
|
||||
* @return the created builder
|
||||
*/
|
||||
public static BodyBuilder method(HttpMethod method, String url,
|
||||
Object... uriVariables) {
|
||||
URI expanded = new UriTemplate(url).expand(uriVariables);
|
||||
return new DefaultBodyBuilder(method, expanded);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a builder with the given method, url, and uri variables.
|
||||
* <p>URI Template variables are expanded using the given URI variables, if any.
|
||||
* @param method the HTTP method (GET, POST, etc)
|
||||
* @param url the URL
|
||||
* @param uriVariables the variables to expand in the template
|
||||
* @return the created builder
|
||||
*/
|
||||
public static BodyBuilder method(HttpMethod method, String url,
|
||||
Map<String, ?> uriVariables) {
|
||||
URI expanded = new UriTemplate(url).expand(uriVariables);
|
||||
return new DefaultBodyBuilder(method, expanded);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a builder with the given method, url, and uri variables.
|
||||
* @param method the HTTP method (GET, POST, etc)
|
||||
* @param url the URL
|
||||
* @return the created builder
|
||||
*/
|
||||
public static BodyBuilder method(HttpMethod method, URI url) {
|
||||
return new DefaultBodyBuilder(method, url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a GET builder with the given url and uri variables.
|
||||
* <p>URI Template variables are expanded using the given URI variables, if any.
|
||||
* @param url the URL
|
||||
* @param uriVariables the variables to expand in the template
|
||||
* @return the created builder
|
||||
*/
|
||||
public static HeadersBuilder<?> get(String url, Object... uriVariables) {
|
||||
return method(HttpMethod.GET, url, uriVariables);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a GET builder with the given url and uri variables.
|
||||
* <p>URI Template variables are expanded using the given URI variables, if any.
|
||||
* @param url the URL
|
||||
* @param uriVariables the variables to expand in the template
|
||||
* @return the created builder
|
||||
*/
|
||||
public static HeadersBuilder<?> get(String url, Map<String, ?> uriVariables) {
|
||||
return method(HttpMethod.GET, url, uriVariables);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a GET builder with the given url.
|
||||
* <p>URI Template variables are expanded using the given URI variables, if any.
|
||||
* @param url the URL
|
||||
* @return the created builder
|
||||
*/
|
||||
public static HeadersBuilder<?> get(URI url) {
|
||||
return method(HttpMethod.GET, url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a HEAD builder with the given url and uri variables.
|
||||
* <p>URI Template variables are expanded using the given URI variables, if any.
|
||||
* @param url the URL
|
||||
* @param uriVariables the variables to expand in the template
|
||||
* @return the created builder
|
||||
*/
|
||||
public static HeadersBuilder<?> head(String url, Object... uriVariables) {
|
||||
return method(HttpMethod.HEAD, url, uriVariables);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a HEAD builder with the given url and uri variables.
|
||||
* <p>URI Template variables are expanded using the given URI variables, if any.
|
||||
* @param url the URL
|
||||
* @param uriVariables the variables to expand in the template
|
||||
* @return the created builder
|
||||
*/
|
||||
public static HeadersBuilder<?> head(String url, Map<String, ?> uriVariables) {
|
||||
return method(HttpMethod.HEAD, url, uriVariables);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a HEAD builder with the given url.
|
||||
* @param url the URL
|
||||
* @return the created builder
|
||||
*/
|
||||
public static HeadersBuilder<?> head(URI url) {
|
||||
return method(HttpMethod.HEAD, url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a POST builder with the given url and uri variables.
|
||||
* <p>URI Template variables are expanded using the given URI variables, if any.
|
||||
* @param url the URL
|
||||
* @param uriVariables the variables to expand in the template
|
||||
* @return the created builder
|
||||
*/
|
||||
public static BodyBuilder post(String url, Object... uriVariables) {
|
||||
return method(HttpMethod.POST, url, uriVariables);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a POST builder with the given url and uri variables.
|
||||
* <p>URI Template variables are expanded using the given URI variables, if any.
|
||||
* @param url the URL
|
||||
* @param uriVariables the variables to expand in the template
|
||||
* @return the created builder
|
||||
*/
|
||||
public static BodyBuilder post(String url, Map<String, ?> uriVariables) {
|
||||
return method(HttpMethod.POST, url, uriVariables);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a POST builder with the given url.
|
||||
* @param url the URL
|
||||
* @return the created builder
|
||||
*/
|
||||
public static BodyBuilder post(URI url) {
|
||||
return method(HttpMethod.POST, url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a PUT builder with the given url and uri variables.
|
||||
* <p>URI Template variables are expanded using the given URI variables, if any.
|
||||
* @param url the URL
|
||||
* @param uriVariables the variables to expand in the template
|
||||
* @return the created builder
|
||||
*/
|
||||
public static BodyBuilder put(String url,
|
||||
Object... uriVariables) {
|
||||
return method(HttpMethod.PUT, url, uriVariables);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a PUT builder with the given url and uri variables.
|
||||
* <p>URI Template variables are expanded using the given URI variables, if any.
|
||||
* @param url the URL
|
||||
* @param uriVariables the variables to expand in the template
|
||||
* @return the created builder
|
||||
*/
|
||||
public static BodyBuilder put(String url,
|
||||
Map<String, ?> uriVariables) {
|
||||
return method(HttpMethod.PUT, url, uriVariables);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a PUT builder with the given url.
|
||||
* @param url the URL
|
||||
* @return the created builder
|
||||
*/
|
||||
public static BodyBuilder put(URI url) {
|
||||
return method(HttpMethod.PUT, url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a PATCH builder with the given url and uri variables.
|
||||
* <p>URI Template variables are expanded using the given URI variables, if any.
|
||||
* @param url the URL
|
||||
* @param uriVariables the variables to expand in the template
|
||||
* @return the created builder
|
||||
*/
|
||||
public static BodyBuilder patch(String url,
|
||||
Object... uriVariables) {
|
||||
return method(HttpMethod.PATCH, url, uriVariables);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a PATCH builder with the given url and uri variables.
|
||||
* <p>URI Template variables are expanded using the given URI variables, if any.
|
||||
* @param url the URL
|
||||
* @param uriVariables the variables to expand in the template
|
||||
* @return the created builder
|
||||
*/
|
||||
public static BodyBuilder patch(String url,
|
||||
Map<String, ?> uriVariables) {
|
||||
return method(HttpMethod.PATCH, url, uriVariables);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a PATCH builder with the given url.
|
||||
* @param url the URL
|
||||
* @return the created builder
|
||||
*/
|
||||
public static BodyBuilder patch(URI url) {
|
||||
return method(HttpMethod.PATCH, url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a DELETE builder with the given url and uri variables.
|
||||
* <p>URI Template variables are expanded using the given URI variables, if any.
|
||||
* @param url the URL
|
||||
* @param uriVariables the variables to expand in the template
|
||||
* @return the created builder
|
||||
*/
|
||||
public static HeadersBuilder<?> delete(String url,
|
||||
Object... uriVariables) {
|
||||
return method(HttpMethod.DELETE, url, uriVariables);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a DELETE builder with the given url and uri variables.
|
||||
* <p>URI Template variables are expanded using the given URI variables, if any.
|
||||
* @param url the URL
|
||||
* @param uriVariables the variables to expand in the template
|
||||
* @return the created builder
|
||||
*/
|
||||
public static HeadersBuilder<?> delete(String url,
|
||||
Map<String, ?> uriVariables) {
|
||||
return method(HttpMethod.DELETE, url, uriVariables);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a DELETE builder with the given url.
|
||||
* @param url the URL
|
||||
* @return the created builder
|
||||
*/
|
||||
public static HeadersBuilder<?> delete(URI url) {
|
||||
return method(HttpMethod.DELETE, url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an OPTIONS builder with the given url and uri variables.
|
||||
* <p>URI Template variables are expanded using the given URI variables, if any.
|
||||
* @param url the URL
|
||||
* @param uriVariables the variables to expand in the template
|
||||
* @return the created builder
|
||||
*/
|
||||
public static HeadersBuilder<?> options(String url,
|
||||
Object... uriVariables) {
|
||||
return method(HttpMethod.OPTIONS, url, uriVariables);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an OPTIONS builder with the given url and uri variables.
|
||||
* <p>URI Template variables are expanded using the given URI variables, if any.
|
||||
* @param url the URL
|
||||
* @param uriVariables the variables to expand in the template
|
||||
* @return the created builder
|
||||
*/
|
||||
public static HeadersBuilder<?> options(String url,
|
||||
Map<String, ?> uriVariables) {
|
||||
return method(HttpMethod.OPTIONS, url, uriVariables);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an OPTIONS builder with the given url.
|
||||
* @param url the URL
|
||||
* @return the created builder
|
||||
*/
|
||||
public static HeadersBuilder<?> options(URI url) {
|
||||
return method(HttpMethod.OPTIONS, url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines a builder that adds headers to the request entity.
|
||||
* @param <B> the builder subclass
|
||||
*/
|
||||
public interface HeadersBuilder<B extends HeadersBuilder<B>> {
|
||||
|
||||
/**
|
||||
* Add the given, single header value under the given name.
|
||||
* @param headerName the header name
|
||||
* @param headerValues the header value(s)
|
||||
* @return this builder
|
||||
* @see HttpHeaders#add(String, String)
|
||||
*/
|
||||
B header(String headerName, String... headerValues);
|
||||
|
||||
/**
|
||||
* Set the list of acceptable {@linkplain MediaType media types}, as specified
|
||||
* by the {@code Accept} header.
|
||||
* @param acceptableMediaTypes the acceptable media types
|
||||
*/
|
||||
B accept(MediaType... acceptableMediaTypes);
|
||||
|
||||
/**
|
||||
* Set the list of acceptable {@linkplain Charset charsets}, as specified by
|
||||
* the {@code Accept-Charset} header.
|
||||
* @param acceptableCharsets the acceptable charsets
|
||||
*/
|
||||
B acceptCharset(Charset... acceptableCharsets);
|
||||
|
||||
/**
|
||||
* Sets the value of the {@code If-Modified-Since} header.
|
||||
* <p>The date should be specified as the number of milliseconds since January 1,
|
||||
* 1970 GMT.
|
||||
* @param ifModifiedSince the new value of the header
|
||||
*/
|
||||
B ifModifiedSince(long ifModifiedSince);
|
||||
|
||||
/**
|
||||
* Sets the values of the {@code If-None-Match} header.
|
||||
* @param ifNoneMatches the new value of the header
|
||||
*/
|
||||
B ifNoneMatch(String... ifNoneMatches);
|
||||
|
||||
/**
|
||||
* Builds the request entity with no body.
|
||||
* @return the request entity
|
||||
* @see BodyBuilder#body(Object)
|
||||
*/
|
||||
RequestEntity<Void> build();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines a builder that adds a body to the response entity.
|
||||
*/
|
||||
public interface BodyBuilder extends HeadersBuilder<BodyBuilder> {
|
||||
|
||||
/**
|
||||
* Set the length of the body in bytes, as specified by the {@code Content-Length}
|
||||
* header.
|
||||
* @param contentLength the content length
|
||||
* @return this builder
|
||||
* @see HttpHeaders#setContentLength(long)
|
||||
*/
|
||||
BodyBuilder contentLength(long contentLength);
|
||||
|
||||
/**
|
||||
* Set the {@linkplain MediaType media type} of the body, as specified by the
|
||||
* {@code Content-Type} header.
|
||||
* @param contentType the content type
|
||||
* @return this builder
|
||||
* @see HttpHeaders#setContentType(MediaType)
|
||||
*/
|
||||
BodyBuilder contentType(MediaType contentType);
|
||||
|
||||
/**
|
||||
* Sets the body of the request entity and returns it.
|
||||
* @param body the body of the request entity
|
||||
* @param <T> the type of the body
|
||||
* @return the built request entity
|
||||
*/
|
||||
<T> RequestEntity<T> body(T body);
|
||||
|
||||
}
|
||||
|
||||
private static class DefaultBodyBuilder implements BodyBuilder {
|
||||
|
||||
private final HttpMethod method;
|
||||
|
||||
private final URI url;
|
||||
|
||||
private final HttpHeaders headers = new HttpHeaders();
|
||||
|
||||
|
||||
public DefaultBodyBuilder(HttpMethod method, URI url) {
|
||||
this.method = method;
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BodyBuilder header(String headerName, String... headerValues) {
|
||||
for (String headerValue : headerValues) {
|
||||
this.headers.add(headerName, headerValue);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BodyBuilder accept(MediaType... acceptableMediaTypes) {
|
||||
this.headers.setAccept(Arrays.asList(acceptableMediaTypes));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BodyBuilder acceptCharset(Charset... acceptableCharsets) {
|
||||
this.headers.setAcceptCharset(Arrays.asList(acceptableCharsets));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BodyBuilder contentLength(long contentLength) {
|
||||
this.headers.setContentLength(contentLength);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BodyBuilder contentType(MediaType contentType) {
|
||||
this.headers.setContentType(contentType);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BodyBuilder ifModifiedSince(long ifModifiedSince) {
|
||||
this.headers.setIfModifiedSince(ifModifiedSince);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BodyBuilder ifNoneMatch(String... ifNoneMatches) {
|
||||
this.headers.setIfNoneMatch(Arrays.asList(ifNoneMatches));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RequestEntity<Void> build() {
|
||||
return new RequestEntity<Void>(null, this.headers, this.method, this.url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> RequestEntity<T> body(T body) {
|
||||
return new RequestEntity<T>(body, this.headers, this.method, this.url);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -24,6 +24,7 @@ import org.springframework.core.ParameterizedTypeReference;
|
|||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.RequestEntity;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
|
||||
/**
|
||||
|
@ -461,6 +462,46 @@ public interface RestOperations {
|
|||
<T> ResponseEntity<T> exchange(URI url, HttpMethod method, HttpEntity<?> requestEntity,
|
||||
ParameterizedTypeReference<T> responseType) throws RestClientException;
|
||||
|
||||
/**
|
||||
* Execute the HTTP method and URL of the {@link RequestEntity}, writing it to the
|
||||
* request, and returns the response as {@link ResponseEntity}. Typically used in
|
||||
* combination with the static builder methods on {@code RequestEntity}, for instance:
|
||||
*
|
||||
* <pre class="code">
|
||||
* MyRequest body = ...
|
||||
* RequestEntity request = RequestEntity.post("http://example.com/{foo}", "bar").accept(MediaType.APPLICATION_JSON).body(body);
|
||||
* ResponseEntity<MyResponse> response = template.exchange(request, MyResponse.class);
|
||||
* </pre>
|
||||
*
|
||||
* @param requestEntity the entity to write to the request
|
||||
* @param responseType the type of the return value
|
||||
* @return the response as entity
|
||||
* @since 4.1
|
||||
*/
|
||||
<T> ResponseEntity<T> exchange(RequestEntity<?> requestEntity,
|
||||
Class<T> responseType) throws RestClientException;
|
||||
|
||||
/**
|
||||
* Execute the HTTP method and URL of the {@link RequestEntity}, writing it to the
|
||||
* request, and returns the response as {@link ResponseEntity}.
|
||||
* The given {@link ParameterizedTypeReference} is used to pass generic type information:
|
||||
*
|
||||
* <pre class="code">
|
||||
* MyRequest body = ...
|
||||
* RequestEntity request = RequestEntity.post("http://example.com/{foo}", "bar").accept(MediaType.APPLICATION_JSON).body(body);
|
||||
* ParameterizedTypeReference<List<MyResponse>> myBean = new ParameterizedTypeReference<List<MyResponse>>() {};
|
||||
* ResponseEntity<List<MyResponse>> response = template.exchange(request, myBean);
|
||||
* </pre>
|
||||
*
|
||||
* @param requestEntity the entity to write to the request
|
||||
* @param responseType the type of the return value
|
||||
* @return the response as entity
|
||||
* @since 4.1
|
||||
*/
|
||||
<T> ResponseEntity<T> exchange(RequestEntity<?> requestEntity,
|
||||
ParameterizedTypeReference<T> responseType) throws RestClientException;
|
||||
|
||||
|
||||
// general execution
|
||||
|
||||
/**
|
||||
|
|
|
@ -31,6 +31,7 @@ import org.springframework.http.HttpEntity;
|
|||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.RequestEntity;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.client.ClientHttpRequest;
|
||||
import org.springframework.http.client.ClientHttpRequestFactory;
|
||||
|
@ -493,6 +494,28 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat
|
|||
return execute(url, method, requestCallback, responseExtractor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ResponseEntity<T> exchange(RequestEntity<?> requestEntity,
|
||||
Class<T> responseType) throws RestClientException {
|
||||
Assert.notNull(requestEntity, "'requestEntity' must not be null");
|
||||
|
||||
RequestCallback requestCallback = httpEntityCallback(requestEntity, responseType);
|
||||
ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
|
||||
return execute(requestEntity.getUrl(), requestEntity.getMethod(), requestCallback, responseExtractor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ResponseEntity<T> exchange(RequestEntity<?> requestEntity,
|
||||
ParameterizedTypeReference<T> responseType) throws RestClientException {
|
||||
Assert.notNull(requestEntity, "'requestEntity' must not be null");
|
||||
|
||||
Type type = responseType.getType();
|
||||
RequestCallback requestCallback = httpEntityCallback(requestEntity, type);
|
||||
ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(type);
|
||||
return execute(requestEntity.getUrl(), requestEntity.getMethod(), requestCallback, responseExtractor);
|
||||
}
|
||||
|
||||
|
||||
// general execution
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,147 @@
|
|||
/*
|
||||
* Copyright 2002-2014 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.http;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import org.junit.Test;
|
||||
|
||||
public class RequestEntityTests {
|
||||
|
||||
@Test
|
||||
public void normal() throws URISyntaxException {
|
||||
String headerName = "My-Custom-Header";
|
||||
String headerValue = "HeaderValue";
|
||||
URI url = new URI("http://example.com");
|
||||
Integer entity = 42;
|
||||
|
||||
RequestEntity<Object> requestEntity =
|
||||
RequestEntity.method(HttpMethod.GET, url)
|
||||
.header(headerName, headerValue).body(entity);
|
||||
|
||||
assertNotNull(requestEntity);
|
||||
assertEquals(HttpMethod.GET, requestEntity.getMethod());
|
||||
assertTrue(requestEntity.getHeaders().containsKey(headerName));
|
||||
assertEquals(headerValue, requestEntity.getHeaders().getFirst(headerName));
|
||||
assertEquals(entity, requestEntity.getBody());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void uriVariablesExpansion() throws URISyntaxException {
|
||||
RequestEntity.get("http://example.com/{foo}", "bar").accept(MediaType.TEXT_PLAIN).build();
|
||||
|
||||
String url = "http://www.{host}.com/{path}";
|
||||
String host = "example";
|
||||
String path = "foo/bar";
|
||||
|
||||
URI expected = new URI("http://www.example.com/foo/bar");
|
||||
|
||||
RequestEntity<?> entity = RequestEntity.method(HttpMethod.GET, url, host, path).build();
|
||||
assertEquals(expected, entity.getUrl());
|
||||
|
||||
Map<String, String> uriVariables = new HashMap<String, String>(2);
|
||||
uriVariables.put("host", host);
|
||||
uriVariables.put("path", path);
|
||||
|
||||
entity = RequestEntity.method(HttpMethod.GET, url, uriVariables).build();
|
||||
assertEquals(expected, entity.getUrl());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void get() {
|
||||
RequestEntity<Void> requestEntity = RequestEntity.get(URI.create("http://example.com")).accept(
|
||||
MediaType.IMAGE_GIF, MediaType.IMAGE_JPEG, MediaType.IMAGE_PNG).build();
|
||||
|
||||
assertNotNull(requestEntity);
|
||||
assertEquals(HttpMethod.GET, requestEntity.getMethod());
|
||||
assertTrue(requestEntity.getHeaders().containsKey("Accept"));
|
||||
assertEquals("image/gif, image/jpeg, image/png", requestEntity.getHeaders().getFirst("Accept"));
|
||||
assertNull(requestEntity.getBody());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void headers() throws URISyntaxException {
|
||||
MediaType accept = MediaType.TEXT_PLAIN;
|
||||
Charset charset = Charset.forName("UTF-8");
|
||||
long ifModifiedSince = 12345L;
|
||||
String ifNoneMatch = "\"foo\"";
|
||||
long contentLength = 67890;
|
||||
MediaType contentType = MediaType.TEXT_PLAIN;
|
||||
|
||||
RequestEntity<Void> responseEntity = RequestEntity.post("http://example.com").
|
||||
accept(accept).
|
||||
acceptCharset(charset).
|
||||
ifModifiedSince(ifModifiedSince).
|
||||
ifNoneMatch(ifNoneMatch).
|
||||
contentLength(contentLength).
|
||||
contentType(contentType).
|
||||
build();
|
||||
|
||||
assertNotNull(responseEntity);
|
||||
assertEquals(HttpMethod.POST, responseEntity.getMethod());
|
||||
assertEquals(new URI("http://example.com"), responseEntity.getUrl());
|
||||
HttpHeaders responseHeaders = responseEntity.getHeaders();
|
||||
|
||||
assertEquals("text/plain", responseHeaders.getFirst("Accept"));
|
||||
assertEquals("utf-8", responseHeaders.getFirst("Accept-Charset"));
|
||||
assertEquals("Thu, 01 Jan 1970 00:00:12 GMT",
|
||||
responseHeaders.getFirst("If-Modified-Since"));
|
||||
assertEquals(ifNoneMatch, responseHeaders.getFirst("If-None-Match"));
|
||||
assertEquals(String.valueOf(contentLength), responseHeaders.getFirst("Content-Length"));
|
||||
assertEquals(contentType.toString(), responseHeaders.getFirst("Content-Type"));
|
||||
|
||||
assertNull(responseEntity.getBody());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void methods() throws URISyntaxException {
|
||||
URI url = new URI("http://example.com");
|
||||
|
||||
RequestEntity<?> entity = RequestEntity.get(url).build();
|
||||
assertEquals(HttpMethod.GET, entity.getMethod());
|
||||
|
||||
entity = RequestEntity.post(url).build();
|
||||
assertEquals(HttpMethod.POST, entity.getMethod());
|
||||
|
||||
entity = RequestEntity.head(url).build();
|
||||
assertEquals(HttpMethod.HEAD, entity.getMethod());
|
||||
|
||||
entity = RequestEntity.options(url).build();
|
||||
assertEquals(HttpMethod.OPTIONS, entity.getMethod());
|
||||
|
||||
entity = RequestEntity.put(url).build();
|
||||
assertEquals(HttpMethod.PUT, entity.getMethod());
|
||||
|
||||
entity = RequestEntity.patch(url).build();
|
||||
assertEquals(HttpMethod.PATCH, entity.getMethod());
|
||||
|
||||
entity = RequestEntity.delete(url).build();
|
||||
assertEquals(HttpMethod.DELETE, entity.getMethod());
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -25,6 +25,7 @@ import org.springframework.core.MethodParameter;
|
|||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpInputMessage;
|
||||
import org.springframework.http.RequestEntity;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.server.ServletServerHttpRequest;
|
||||
|
@ -68,12 +69,14 @@ public class HttpEntityMethodProcessor extends AbstractMessageConverterMethodPro
|
|||
|
||||
@Override
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
return HttpEntity.class.equals(parameter.getParameterType());
|
||||
return HttpEntity.class.equals(parameter.getParameterType()) ||
|
||||
RequestEntity.class.equals(parameter.getParameterType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsReturnType(MethodParameter returnType) {
|
||||
return HttpEntity.class.isAssignableFrom(returnType.getParameterType());
|
||||
return HttpEntity.class.equals(returnType.getParameterType()) ||
|
||||
ResponseEntity.class.equals(returnType.getParameterType());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -81,11 +84,18 @@ public class HttpEntityMethodProcessor extends AbstractMessageConverterMethodPro
|
|||
NativeWebRequest webRequest, WebDataBinderFactory binderFactory)
|
||||
throws IOException, HttpMediaTypeNotSupportedException {
|
||||
|
||||
HttpInputMessage inputMessage = createInputMessage(webRequest);
|
||||
ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
|
||||
Type paramType = getHttpEntityType(parameter);
|
||||
|
||||
Object body = readWithMessageConverters(webRequest, parameter, paramType);
|
||||
return new HttpEntity<Object>(body, inputMessage.getHeaders());
|
||||
if (RequestEntity.class.equals(parameter.getParameterType())) {
|
||||
return new RequestEntity<Object>(body, inputMessage.getHeaders(),
|
||||
inputMessage.getMethod(), inputMessage.getURI());
|
||||
}
|
||||
else {
|
||||
return new HttpEntity<Object>(body, inputMessage.getHeaders());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private Type getHttpEntityType(MethodParameter parameter) {
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
package org.springframework.web.servlet.mvc.method.annotation;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URI;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
|
@ -27,9 +28,11 @@ import org.springframework.core.MethodParameter;
|
|||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpInputMessage;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpOutputMessage;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.RequestEntity;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.mock.web.test.MockHttpServletRequest;
|
||||
|
@ -65,6 +68,7 @@ public class HttpEntityMethodProcessorMockTests {
|
|||
private MethodParameter returnTypeResponseEntity;
|
||||
private MethodParameter returnTypeHttpEntity;
|
||||
private MethodParameter returnTypeInt;
|
||||
private MethodParameter paramRequestEntity;
|
||||
private MethodParameter returnTypeResponseEntityProduces;
|
||||
|
||||
private ModelAndViewContainer mavContainer;
|
||||
|
@ -75,6 +79,7 @@ public class HttpEntityMethodProcessorMockTests {
|
|||
|
||||
private MockHttpServletRequest servletRequest;
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
|
@ -85,10 +90,11 @@ public class HttpEntityMethodProcessorMockTests {
|
|||
reset(messageConverter);
|
||||
|
||||
|
||||
Method handle1 = getClass().getMethod("handle1", HttpEntity.class, ResponseEntity.class, Integer.TYPE);
|
||||
Method handle1 = getClass().getMethod("handle1", HttpEntity.class, ResponseEntity.class, Integer.TYPE, RequestEntity.class);
|
||||
paramHttpEntity = new MethodParameter(handle1, 0);
|
||||
paramResponseEntity = new MethodParameter(handle1, 1);
|
||||
paramInt = new MethodParameter(handle1, 2);
|
||||
paramRequestEntity = new MethodParameter(handle1, 3);
|
||||
returnTypeResponseEntity = new MethodParameter(handle1, -1);
|
||||
|
||||
returnTypeHttpEntity = new MethodParameter(getClass().getMethod("handle2", HttpEntity.class), -1);
|
||||
|
@ -107,6 +113,7 @@ public class HttpEntityMethodProcessorMockTests {
|
|||
@Test
|
||||
public void supportsParameter() {
|
||||
assertTrue("HttpEntity parameter not supported", processor.supportsParameter(paramHttpEntity));
|
||||
assertTrue("RequestEntity parameter not supported", processor.supportsParameter(paramRequestEntity));
|
||||
assertFalse("ResponseEntity parameter supported", processor.supportsParameter(paramResponseEntity));
|
||||
assertFalse("non-entity parameter supported", processor.supportsParameter(paramInt));
|
||||
}
|
||||
|
@ -115,6 +122,8 @@ public class HttpEntityMethodProcessorMockTests {
|
|||
public void supportsReturnType() {
|
||||
assertTrue("ResponseEntity return type not supported", processor.supportsReturnType(returnTypeResponseEntity));
|
||||
assertTrue("HttpEntity return type not supported", processor.supportsReturnType(returnTypeHttpEntity));
|
||||
assertFalse("RequestEntity parameter supported",
|
||||
processor.supportsReturnType(paramRequestEntity));
|
||||
assertFalse("non-ResponseBody return type supported", processor.supportsReturnType(returnTypeInt));
|
||||
}
|
||||
|
||||
|
@ -134,6 +143,29 @@ public class HttpEntityMethodProcessorMockTests {
|
|||
assertEquals("Invalid argument", body, ((HttpEntity<?>) result).getBody());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveArgumentRequestEntity() throws Exception {
|
||||
MediaType contentType = MediaType.TEXT_PLAIN;
|
||||
servletRequest.addHeader("Content-Type", contentType.toString());
|
||||
servletRequest.setMethod("GET");
|
||||
servletRequest.setServerName("www.example.com");
|
||||
servletRequest.setServerPort(80);
|
||||
servletRequest.setRequestURI("/path");
|
||||
|
||||
String body = "Foo";
|
||||
given(messageConverter.canRead(String.class, contentType)).willReturn(true);
|
||||
given(messageConverter.read(eq(String.class), isA(HttpInputMessage.class))).willReturn(body);
|
||||
|
||||
Object result = processor.resolveArgument(paramRequestEntity, mavContainer, webRequest, null);
|
||||
|
||||
assertTrue(result instanceof RequestEntity);
|
||||
assertFalse("The requestHandled flag shouldn't change", mavContainer.isRequestHandled());
|
||||
RequestEntity<?> requestEntity = (RequestEntity<?>) result;
|
||||
assertEquals("Invalid method", HttpMethod.GET, requestEntity.getMethod());
|
||||
assertEquals("Invalid url", new URI("http", null, "www.example.com", 80, "/path", null, null), requestEntity.getUrl());
|
||||
assertEquals("Invalid argument", body, requestEntity.getBody());
|
||||
}
|
||||
|
||||
@Test(expected = HttpMediaTypeNotSupportedException.class)
|
||||
public void resolveArgumentNotReadable() throws Exception {
|
||||
MediaType contentType = MediaType.TEXT_PLAIN;
|
||||
|
@ -262,7 +294,7 @@ public class HttpEntityMethodProcessorMockTests {
|
|||
assertEquals("headerValue", outputMessage.getValue().getHeaders().get("header").get(0));
|
||||
}
|
||||
|
||||
public ResponseEntity<String> handle1(HttpEntity<String> httpEntity, ResponseEntity<String> responseEntity, int i) {
|
||||
public ResponseEntity<String> handle1(HttpEntity<String> httpEntity, ResponseEntity<String> responseEntity, int i, RequestEntity<String> requestEntity) {
|
||||
return responseEntity;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue