diff --git a/org.springframework.web/src/main/java/org/springframework/http/HttpEntity.java b/org.springframework.web/src/main/java/org/springframework/http/HttpEntity.java
new file mode 100644
index 0000000000000000000000000000000000000000..2baf302a74aa8d8928158960869077f24e576b72
--- /dev/null
+++ b/org.springframework.web/src/main/java/org/springframework/http/HttpEntity.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2002-2010 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.util.Map;
+
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+
+/**
+ * Represents an HTTP request or response entity, consisting of headers and body.
+ *
+ *
Typically used in combination with the {@link org.springframework.web.client.RestTemplate RestTemplate}, like so:
+ *
+ * HttpEntity<String> entity = new HttpEntity<String>(helloWorld, MediaType.TEXT_PLAIN);
+ * URI location = template.postForLocation("http://example.com", entity);
+ *
+ *
+ * @author Arjen Poutsma
+ * @see org.springframework.web.client.RestTemplate
+ * @see #getBody()
+ * @see #getHeaders()
+ * @since 3.0.2
+ */
+public class HttpEntity {
+
+ /**
+ * The empty {@code HttpEntity}, with no body or headers.
+ */
+ public static final HttpEntity EMPTY = new HttpEntity();
+
+ private final HttpHeaders headers;
+
+ private final T body;
+
+ /**
+ * Create a new, empty {@code HttpEntity}.
+ */
+ private HttpEntity() {
+ this(null, (MultiValueMap) null);
+ }
+
+ /**
+ * Create a new {@code HttpEntity} with the given body and no headers.
+ *
+ * @param body the entity body
+ */
+ public HttpEntity(T body) {
+ this(body, (MultiValueMap) null);
+ }
+
+ /**
+ * Create a new {@code HttpEntity} with the given headers and no body.
+ *
+ * @param headers the entity headers
+ */
+ public HttpEntity(Map headers) {
+ this(null, toMultiValueMap(headers));
+ }
+
+ /**
+ * Create a new {@code HttpEntity} with the given headers and no body.
+ *
+ * @param headers the entity headers
+ */
+ public HttpEntity(MultiValueMap headers) {
+ this(null, headers);
+ }
+
+ /**
+ * Create a new {@code HttpEntity} with the given body and {@code Content-Type} header value.
+ *
+ * @param body the entity body
+ * @param contentType the value of the {@code Content-Type header}
+ */
+ public HttpEntity(T body, MediaType contentType) {
+ this(body, toMultiValueMap(contentType));
+ }
+
+ /**
+ * Create a new {@code HttpEntity} with the given body and headers.
+ *
+ * @param body the entity body
+ * @param headers the entity headers
+ */
+ public HttpEntity(T body, Map headers) {
+ this(body, toMultiValueMap(headers));
+ }
+
+ /**
+ * Create a new {@code HttpEntity} with the given body and headers.
+ *
+ * @param body the entity body
+ * @param headers the entity headers
+ */
+ public HttpEntity(T body, MultiValueMap headers) {
+ this.body = body;
+ HttpHeaders tempHeaders = new HttpHeaders();
+ if (headers != null) {
+ tempHeaders.putAll(headers);
+ }
+ this.headers = HttpHeaders.readOnlyHttpHeaders(tempHeaders);
+ }
+
+ private static MultiValueMap toMultiValueMap(Map map) {
+ if (map == null) {
+ return null;
+ }
+ else {
+ MultiValueMap result = new LinkedMultiValueMap(map.size());
+ result.setAll(map);
+ return result;
+ }
+ }
+
+ private static MultiValueMap toMultiValueMap(MediaType contentType) {
+ if (contentType == null) {
+ return null;
+ }
+ else {
+ HttpHeaders result = new HttpHeaders();
+ result.setContentType(contentType);
+ return result;
+ }
+ }
+
+ /**
+ * Returns the headers of this entity.
+ */
+ public HttpHeaders getHeaders() {
+ return headers;
+ }
+
+ /**
+ * Returns the body of this entity.
+ */
+ public T getBody() {
+ return body;
+ }
+
+ /**
+ * Indicates whether this entity has a body.
+ */
+ public boolean hasBody() {
+ return body != null;
+ }
+
+}
diff --git a/org.springframework.web/src/main/java/org/springframework/http/converter/AbstractHttpMessageConverter.java b/org.springframework.web/src/main/java/org/springframework/http/converter/AbstractHttpMessageConverter.java
index 5b303a2a5163333d283ab08749f1d03d81898cba..4264803c6c132d40f433e1699c6246fd44e9f904 100644
--- a/org.springframework.web/src/main/java/org/springframework/http/converter/AbstractHttpMessageConverter.java
+++ b/org.springframework.web/src/main/java/org/springframework/http/converter/AbstractHttpMessageConverter.java
@@ -162,15 +162,19 @@ public abstract class AbstractHttpMessageConverter implements HttpMessageConv
throws IOException, HttpMessageNotWritableException {
HttpHeaders headers = outputMessage.getHeaders();
- if (contentType == null || contentType.isWildcardType() || contentType.isWildcardSubtype()) {
- contentType = getDefaultContentType(t);
- }
- if (contentType != null) {
- headers.setContentType(contentType);
+ if (headers.getContentType() == null) {
+ if (contentType == null || contentType.isWildcardType() || contentType.isWildcardSubtype()) {
+ contentType = getDefaultContentType(t);
+ }
+ if (contentType != null) {
+ headers.setContentType(contentType);
+ }
}
- Long contentLength = getContentLength(t, contentType);
- if (contentLength != null) {
- headers.setContentLength(contentLength);
+ if (headers.getContentLength() == -1) {
+ Long contentLength = getContentLength(t, contentType);
+ if (contentLength != null) {
+ headers.setContentLength(contentLength);
+ }
}
writeInternal(t, outputMessage);
outputMessage.getBody().flush();
diff --git a/org.springframework.web/src/main/java/org/springframework/http/converter/FormHttpMessageConverter.java b/org.springframework.web/src/main/java/org/springframework/http/converter/FormHttpMessageConverter.java
index 9a79c5c5b2f9b29df57a1d43739adf5d5a2b666b..a541e422918d4170d61cee636c369bdeeaca9198 100644
--- a/org.springframework.web/src/main/java/org/springframework/http/converter/FormHttpMessageConverter.java
+++ b/org.springframework.web/src/main/java/org/springframework/http/converter/FormHttpMessageConverter.java
@@ -33,6 +33,7 @@ import java.util.Map;
import java.util.Random;
import org.springframework.core.io.Resource;
+import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
@@ -241,7 +242,8 @@ public class FormHttpMessageConverter implements HttpMessageConverter partType = part.getClass();
+ private HttpEntity getEntity(Object part) {
+ if (part instanceof HttpEntity) {
+ return (HttpEntity) part;
+ }
+ else {
+ return new HttpEntity(part);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private void writePart(String name, HttpEntity partEntity, OutputStream os) throws IOException {
+ Object partBody = partEntity.getBody();
+ Class> partType = partBody.getClass();
+ HttpHeaders partHeaders = partEntity.getHeaders();
+ MediaType partContentType = partHeaders.getContentType();
for (HttpMessageConverter messageConverter : partConverters) {
- if (messageConverter.canWrite(partType, null)) {
+ if (messageConverter.canWrite(partType, partContentType)) {
HttpOutputMessage multipartOutputMessage = new MultipartHttpOutputMessage(os);
- multipartOutputMessage.getHeaders().setContentDispositionFormData(name, getFilename(part));
- messageConverter.write(part, null, multipartOutputMessage);
+ multipartOutputMessage.getHeaders().setContentDispositionFormData(name, getFilename(partBody));
+ if (!partHeaders.isEmpty()) {
+ multipartOutputMessage.getHeaders().putAll(partHeaders);
+ }
+ messageConverter.write(partBody, partContentType, multipartOutputMessage);
return;
}
}
diff --git a/org.springframework.web/src/main/java/org/springframework/web/client/RestOperations.java b/org.springframework.web/src/main/java/org/springframework/web/client/RestOperations.java
index 656e8d6c32e360d20dade90928c53789e18e87ee..4107fec77bd8a7781017588a764cf203d95bc990 100644
--- a/org.springframework.web/src/main/java/org/springframework/web/client/RestOperations.java
+++ b/org.springframework.web/src/main/java/org/springframework/web/client/RestOperations.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2009 the original author or authors.
+ * Copyright 2002-2010 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.
@@ -20,6 +20,7 @@ import java.net.URI;
import java.util.Map;
import java.util.Set;
+import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
@@ -68,6 +69,40 @@ public interface RestOperations {
*/
T getForObject(URI url, Class responseType) throws RestClientException;
+ /**
+ * Retrieve an entity by doing a GET on the specified URL.
+ * The response is converted and stored in an {@link HttpEntity}.
+ *
URI Template variables are expanded using the given URI variables, if any.
+ * @param url the URL
+ * @param responseType the type of the return value
+ * @param uriVariables the variables to expand the template
+ * @return the entity
+ * @since 3.0.2
+ */
+ HttpEntity getForEntity(String url, Class responseType, Object... uriVariables) throws RestClientException;
+
+ /**
+ * Retrieve a representation by doing a GET on the URI template.
+ * The response is converted and stored in an {@link HttpEntity}.
+ *
URI Template variables are expanded using the given map.
+ * @param url the URL
+ * @param responseType the type of the return value
+ * @param uriVariables the map containing variables for the URI template
+ * @return the converted object
+ * @since 3.0.2
+ */
+ HttpEntity getForEntity(String url, Class responseType, Map uriVariables) throws RestClientException;
+
+ /**
+ * Retrieve a representation by doing a GET on the URL .
+ * The response is converted and stored in an {@link HttpEntity}.
+ * @param url the URL
+ * @param responseType the type of the return value
+ * @return the converted object
+ * @since 3.0.2
+ */
+ HttpEntity getForEntity(URI url, Class responseType) throws RestClientException;
+
// HEAD
/**
@@ -101,10 +136,13 @@ public interface RestOperations {
* Create a new resource by POSTing the given object to the URI template, and returns the value of the
* Location header. This header typically indicates where the new resource is stored.
*
URI Template variables are expanded using the given URI variables, if any.
+ *
The {@code request} parameter can be a {@link HttpEntity} in order to
+ * add additional HTTP headers to the request.
* @param url the URL
* @param request the Object to be POSTed, may be null
* @param uriVariables the variables to expand the template
* @return the value for the Location header
+ * @see HttpEntity
*/
URI postForLocation(String url, Object request, Object... uriVariables) throws RestClientException;
@@ -112,19 +150,25 @@ public interface RestOperations {
* Create a new resource by POSTing the given object to the URI template, and returns the value of the
* Location header. This header typically indicates where the new resource is stored.
*
URI Template variables are expanded using the given map.
+ *
The {@code request} parameter can be a {@link HttpEntity} in order to
+ * add additional HTTP headers to the request.
* @param url the URL
* @param request the Object to be POSTed, may be null
* @param uriVariables the variables to expand the template
* @return the value for the Location header
+ * @see HttpEntity
*/
URI postForLocation(String url, Object request, Map uriVariables) throws RestClientException;
/**
* Create a new resource by POSTing the given object to the URL, and returns the value of the
* Location header. This header typically indicates where the new resource is stored.
+ *
The {@code request} parameter can be a {@link HttpEntity} in order to
+ * add additional HTTP headers to the request.
* @param url the URL
* @param request the Object to be POSTed, may be null
* @return the value for the Location header
+ * @see HttpEntity
*/
URI postForLocation(URI url, Object request) throws RestClientException;
@@ -132,10 +176,13 @@ public interface RestOperations {
* Create a new resource by POSTing the given object to the URI template,
* and returns the representation found in the response.
*
URI Template variables are expanded using the given URI variables, if any.
+ *
The {@code request} parameter can be a {@link HttpEntity} in order to
+ * add additional HTTP headers to the request.
* @param url the URL
* @param request the Object to be POSTed, may be null
* @param uriVariables the variables to expand the template
* @return the converted object
+ * @see HttpEntity
*/
T postForObject(String url, Object request, Class responseType, Object... uriVariables)
throws RestClientException;
@@ -144,10 +191,13 @@ public interface RestOperations {
* Create a new resource by POSTing the given object to the URI template,
* and returns the representation found in the response.
*
URI Template variables are expanded using the given map.
+ *
The {@code request} parameter can be a {@link HttpEntity} in order to
+ * add additional HTTP headers to the request.
* @param url the URL
* @param request the Object to be POSTed, may be null
* @param uriVariables the variables to expand the template
* @return the converted object
+ * @see HttpEntity
*/
T postForObject(String url, Object request, Class responseType, Map uriVariables)
throws RestClientException;
@@ -155,36 +205,93 @@ public interface RestOperations {
/**
* Create a new resource by POSTing the given object to the URL,
* and returns the representation found in the response.
+ *
The {@code request} parameter can be a {@link HttpEntity} in order to
+ * add additional HTTP headers to the request.
* @param url the URL
* @param request the Object to be POSTed, may be null
* @return the converted object
+ * @see HttpEntity
*/
T postForObject(URI url, Object request, Class responseType) throws RestClientException;
+ /**
+ * Create a new resource by POSTing the given object to the URI template,
+ * and returns the response as {@link HttpEntity}.
+ *
URI Template variables are expanded using the given URI variables, if any.
+ *
The {@code request} parameter can be a {@link HttpEntity} in order to
+ * add additional HTTP headers to the request.
+ * @param url the URL
+ * @param request the Object to be POSTed, may be null
+ * @param uriVariables the variables to expand the template
+ * @return the converted object
+ * @see HttpEntity
+ * @since 3.0.2
+ */
+ HttpEntity postForEntity(String url, Object request, Class responseType, Object... uriVariables)
+ throws RestClientException;
+
+ /**
+ * Create a new resource by POSTing the given object to the URI template,
+ * and returns the response as {@link HttpEntity}.
+ *
URI Template variables are expanded using the given map.
+ *
The {@code request} parameter can be a {@link HttpEntity} in order to
+ * add additional HTTP headers to the request.
+ * @param url the URL
+ * @param request the Object to be POSTed, may be null
+ * @param uriVariables the variables to expand the template
+ * @return the converted object
+ * @see HttpEntity
+ * @since 3.0.2
+ */
+ HttpEntity postForEntity(String url, Object request, Class responseType, Map uriVariables)
+ throws RestClientException;
+
+ /**
+ * Create a new resource by POSTing the given object to the URL,
+ * and returns the response as {@link HttpEntity}.
+ *
The {@code request} parameter can be a {@link HttpEntity} in order to
+ * add additional HTTP headers to the request.
+ * @param url the URL
+ * @param request the Object to be POSTed, may be null
+ * @return the converted object
+ * @see HttpEntity
+ * @since 3.0.2
+ */
+ HttpEntity postForEntity(URI url, Object request, Class responseType) throws RestClientException;
+
// PUT
/**
* Create or update a resource by PUTting the given object to the URI.
*
URI Template variables are expanded using the given URI variables, if any.
+ *
The {@code request} parameter can be a {@link HttpEntity} in order to
+ * add additional HTTP headers to the request.
* @param url the URL
* @param request the Object to be PUT, may be null
* @param uriVariables the variables to expand the template
+ * @see HttpEntity
*/
void put(String url, Object request, Object... uriVariables) throws RestClientException;
/**
* Creates a new resource by PUTting the given object to URI template.
*
URI Template variables are expanded using the given map.
+ *
The {@code request} parameter can be a {@link HttpEntity} in order to
+ * add additional HTTP headers to the request.
* @param url the URL
* @param request the Object to be PUT, may be null
* @param uriVariables the variables to expand the template
+ * @see HttpEntity
*/
void put(String url, Object request, Map uriVariables) throws RestClientException;
/**
* Creates a new resource by PUTting the given object to URL.
+ *
The {@code request} parameter can be a {@link HttpEntity} in order to
+ * add additional HTTP headers to the request.
* @param url the URL
* @param request the Object to be PUT, may be null
+ * @see HttpEntity
*/
void put(URI url, Object request) throws RestClientException;
diff --git a/org.springframework.web/src/main/java/org/springframework/web/client/RestTemplate.java b/org.springframework.web/src/main/java/org/springframework/web/client/RestTemplate.java
index 72342e1aebd815425d3950ccb63e537133c94775..cc9ec8ad7b694ce395f9ca2cd15c4f4b60c08a66 100644
--- a/org.springframework.web/src/main/java/org/springframework/web/client/RestTemplate.java
+++ b/org.springframework.web/src/main/java/org/springframework/web/client/RestTemplate.java
@@ -25,6 +25,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
+import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
@@ -184,26 +185,49 @@ public class RestTemplate extends HttpAccessor implements RestOperations {
// GET
public T getForObject(String url, Class responseType, Object... urlVariables) throws RestClientException {
- AcceptHeaderRequestCallback requestCallback = new AcceptHeaderRequestCallback(responseType);
+ AcceptHeaderRequestCallback requestCallback = new AcceptHeaderRequestCallback(responseType);
HttpMessageConverterExtractor responseExtractor =
new HttpMessageConverterExtractor(responseType, getMessageConverters());
return execute(url, HttpMethod.GET, requestCallback, responseExtractor, urlVariables);
}
public T getForObject(String url, Class responseType, Map urlVariables) throws RestClientException {
- AcceptHeaderRequestCallback requestCallback = new AcceptHeaderRequestCallback(responseType);
+ AcceptHeaderRequestCallback requestCallback = new AcceptHeaderRequestCallback(responseType);
HttpMessageConverterExtractor responseExtractor =
new HttpMessageConverterExtractor(responseType, getMessageConverters());
return execute(url, HttpMethod.GET, requestCallback, responseExtractor, urlVariables);
}
public T getForObject(URI url, Class responseType) throws RestClientException {
- AcceptHeaderRequestCallback requestCallback = new AcceptHeaderRequestCallback(responseType);
+ AcceptHeaderRequestCallback requestCallback = new AcceptHeaderRequestCallback(responseType);
HttpMessageConverterExtractor responseExtractor =
new HttpMessageConverterExtractor(responseType, getMessageConverters());
return execute(url, HttpMethod.GET, requestCallback, responseExtractor);
}
+ public HttpEntity getForEntity(String url, Class responseType, Object... urlVariables)
+ throws RestClientException {
+ AcceptHeaderRequestCallback requestCallback = new AcceptHeaderRequestCallback(responseType);
+ HttpEntityResponseExtractor responseExtractor =
+ new HttpEntityResponseExtractor(responseType);
+ return execute(url, HttpMethod.GET, requestCallback, responseExtractor, urlVariables);
+ }
+
+ public HttpEntity getForEntity(String url, Class responseType, Map urlVariables)
+ throws RestClientException {
+ AcceptHeaderRequestCallback requestCallback = new AcceptHeaderRequestCallback(responseType);
+ HttpEntityResponseExtractor responseExtractor =
+ new HttpEntityResponseExtractor(responseType);
+ return execute(url, HttpMethod.GET, requestCallback, responseExtractor, urlVariables);
+ }
+
+ public HttpEntity getForEntity(URI url, Class responseType) throws RestClientException {
+ AcceptHeaderRequestCallback requestCallback = new AcceptHeaderRequestCallback(responseType);
+ HttpEntityResponseExtractor responseExtractor =
+ new HttpEntityResponseExtractor(responseType);
+ return execute(url, HttpMethod.GET, requestCallback, responseExtractor);
+ }
+
// HEAD
public HttpHeaders headForHeaders(String url, Object... urlVariables) throws RestClientException {
@@ -221,27 +245,27 @@ public class RestTemplate extends HttpAccessor implements RestOperations {
// POST
public URI postForLocation(String url, Object request, Object... urlVariables) throws RestClientException {
- PostPutCallback requestCallback = new PostPutCallback(request);
+ HttpEntityRequestCallback requestCallback = new HttpEntityRequestCallback(request);
HttpHeaders headers = execute(url, HttpMethod.POST, requestCallback, this.headersExtractor, urlVariables);
return headers.getLocation();
}
public URI postForLocation(String url, Object request, Map urlVariables)
throws RestClientException {
- PostPutCallback requestCallback = new PostPutCallback(request);
+ HttpEntityRequestCallback requestCallback = new HttpEntityRequestCallback(request);
HttpHeaders headers = execute(url, HttpMethod.POST, requestCallback, this.headersExtractor, urlVariables);
return headers.getLocation();
}
public URI postForLocation(URI url, Object request) throws RestClientException {
- PostPutCallback requestCallback = new PostPutCallback(request);
+ HttpEntityRequestCallback requestCallback = new HttpEntityRequestCallback(request);
HttpHeaders headers = execute(url, HttpMethod.POST, requestCallback, this.headersExtractor);
return headers.getLocation();
}
public T postForObject(String url, Object request, Class responseType, Object... uriVariables)
throws RestClientException {
- PostPutCallback requestCallback = new PostPutCallback(request, responseType);
+ HttpEntityRequestCallback requestCallback = new HttpEntityRequestCallback(request, responseType);
HttpMessageConverterExtractor responseExtractor =
new HttpMessageConverterExtractor(responseType, getMessageConverters());
return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables);
@@ -249,33 +273,59 @@ public class RestTemplate extends HttpAccessor implements RestOperations {
public T postForObject(String url, Object request, Class responseType, Map uriVariables)
throws RestClientException {
- PostPutCallback requestCallback = new PostPutCallback(request, responseType);
+ HttpEntityRequestCallback requestCallback = new HttpEntityRequestCallback(request, responseType);
HttpMessageConverterExtractor responseExtractor =
new HttpMessageConverterExtractor(responseType, getMessageConverters());
return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables);
}
public T postForObject(URI url, Object request, Class responseType) throws RestClientException {
- PostPutCallback requestCallback = new PostPutCallback(request, responseType);
+ HttpEntityRequestCallback requestCallback = new HttpEntityRequestCallback(request, responseType);
HttpMessageConverterExtractor responseExtractor =
new HttpMessageConverterExtractor(responseType, getMessageConverters());
return execute(url, HttpMethod.POST, requestCallback, responseExtractor);
}
+ public HttpEntity postForEntity(String url, Object request, Class responseType, Object... uriVariables)
+ throws RestClientException {
+ HttpEntityRequestCallback requestCallback = new HttpEntityRequestCallback(request, responseType);
+ HttpEntityResponseExtractor responseExtractor =
+ new HttpEntityResponseExtractor(responseType);
+ return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables);
+ }
+
+ public HttpEntity postForEntity(String url,
+ Object request,
+ Class responseType,
+ Map uriVariables)
+ throws RestClientException {
+ HttpEntityRequestCallback requestCallback = new HttpEntityRequestCallback(request, responseType);
+ HttpEntityResponseExtractor responseExtractor =
+ new HttpEntityResponseExtractor(responseType);
+ return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables);
+ }
+
+ public HttpEntity postForEntity(URI url, Object request, Class responseType) throws RestClientException {
+ HttpEntityRequestCallback requestCallback = new HttpEntityRequestCallback(request, responseType);
+ HttpEntityResponseExtractor responseExtractor =
+ new HttpEntityResponseExtractor(responseType);
+ return execute(url, HttpMethod.POST, requestCallback, responseExtractor);
+ }
+
// PUT
public void put(String url, Object request, Object... urlVariables) throws RestClientException {
- PostPutCallback requestCallback = new PostPutCallback(request);
+ HttpEntityRequestCallback requestCallback = new HttpEntityRequestCallback(request);
execute(url, HttpMethod.PUT, requestCallback, null, urlVariables);
}
public void put(String url, Object request, Map urlVariables) throws RestClientException {
- PostPutCallback requestCallback = new PostPutCallback(request);
+ HttpEntityRequestCallback requestCallback = new HttpEntityRequestCallback(request);
execute(url, HttpMethod.PUT, requestCallback, null, urlVariables);
}
public void put(URI url, Object request) throws RestClientException {
- PostPutCallback requestCallback = new PostPutCallback(request);
+ HttpEntityRequestCallback requestCallback = new HttpEntityRequestCallback(request);
execute(url, HttpMethod.PUT, requestCallback, null);
}
@@ -409,15 +459,11 @@ public class RestTemplate extends HttpAccessor implements RestOperations {
/**
* Request callback implementation that prepares the request's accept headers.
*/
- private class AcceptHeaderRequestCallback implements RequestCallback {
-
- private final Class responseType;
+ private class AcceptHeaderRequestCallback implements RequestCallback {
- private AcceptHeaderRequestCallback() {
- responseType = null;
- }
+ private final Class> responseType;
- private AcceptHeaderRequestCallback(Class responseType) {
+ private AcceptHeaderRequestCallback(Class> responseType) {
this.responseType = responseType;
}
@@ -449,31 +495,51 @@ public class RestTemplate extends HttpAccessor implements RestOperations {
/**
* Request callback implementation that writes the given object to the request stream.
*/
- private class PostPutCallback extends AcceptHeaderRequestCallback {
-
- private final Object requestBody;
+ private class HttpEntityRequestCallback extends AcceptHeaderRequestCallback {
- private final MediaType requestContentType;
+ private final HttpEntity requestEntity;
- private PostPutCallback(Object requestBody) {
- this.requestBody = requestBody;
- this.requestContentType = null;
+ private HttpEntityRequestCallback(Object requestBody) {
+ this(requestBody, null);
}
- private PostPutCallback(Object requestBody, Class responseType) {
+ @SuppressWarnings("unchecked")
+ private HttpEntityRequestCallback(Object requestBody, Class> responseType) {
super(responseType);
- this.requestBody = requestBody;
- this.requestContentType = null;
+ if (requestBody instanceof HttpEntity) {
+ this.requestEntity = (HttpEntity) requestBody;
+ }
+ else if (requestBody != null) {
+ this.requestEntity = new HttpEntity(requestBody);
+ }
+ else {
+ this.requestEntity = HttpEntity.EMPTY;
+ }
}
@Override
@SuppressWarnings("unchecked")
public void doWithRequest(ClientHttpRequest httpRequest) throws IOException {
super.doWithRequest(httpRequest);
- if (requestBody != null) {
+ if (!requestEntity.hasBody()) {
+ HttpHeaders requestHeaders = requestEntity.getHeaders();
+ if (!requestHeaders.isEmpty()) {
+ httpRequest.getHeaders().putAll(requestHeaders);
+ }
+ if (httpRequest.getHeaders().getContentLength() == -1) {
+ httpRequest.getHeaders().setContentLength(0L);
+ }
+ }
+ else {
+ Object requestBody = requestEntity.getBody();
Class> requestType = requestBody.getClass();
+ HttpHeaders requestHeaders = requestEntity.getHeaders();
+ MediaType requestContentType = requestHeaders.getContentType();
for (HttpMessageConverter messageConverter : getMessageConverters()) {
if (messageConverter.canWrite(requestType, requestContentType)) {
+ if (!requestHeaders.isEmpty()) {
+ httpRequest.getHeaders().putAll(requestHeaders);
+ }
messageConverter.write(requestBody, requestContentType, httpRequest);
return;
}
@@ -485,9 +551,23 @@ public class RestTemplate extends HttpAccessor implements RestOperations {
}
throw new RestClientException(message);
}
- else {
- httpRequest.getHeaders().setContentLength(0L);
- }
+ }
+ }
+
+ /**
+ * Response extractor for {@link HttpEntity}.
+ */
+ private class HttpEntityResponseExtractor implements ResponseExtractor> {
+
+ private final HttpMessageConverterExtractor delegate;
+
+ public HttpEntityResponseExtractor(Class responseType) {
+ this.delegate = new HttpMessageConverterExtractor(responseType, getMessageConverters());
+ }
+
+ public HttpEntity extractData(ClientHttpResponse response) throws IOException {
+ T body = delegate.extractData(response);
+ return new HttpEntity(body, response.getHeaders());
}
}
diff --git a/org.springframework.web/src/test/java/org/springframework/http/HttpEntityTests.java b/org.springframework.web/src/test/java/org/springframework/http/HttpEntityTests.java
new file mode 100644
index 0000000000000000000000000000000000000000..66dc4024226c58fc2f9cd867ed0883142a10fd88
--- /dev/null
+++ b/org.springframework.web/src/test/java/org/springframework/http/HttpEntityTests.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2002-2010 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.util.LinkedHashMap;
+import java.util.Map;
+
+import static org.junit.Assert.*;
+import org.junit.Test;
+
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+
+/**
+ * @author Arjen Poutsma
+ */
+public class HttpEntityTests {
+
+ @Test
+ public void noHeaders() {
+ String body = "foo";
+ HttpEntity entity = new HttpEntity(body);
+ assertSame(body, entity.getBody());
+ assertTrue(entity.getHeaders().isEmpty());
+ }
+
+ @Test
+ public void contentType() {
+ MediaType contentType = MediaType.TEXT_PLAIN;
+ HttpEntity entity = new HttpEntity("foo", contentType);
+ assertEquals(contentType, entity.getHeaders().getContentType());
+ assertEquals("text/plain", entity.getHeaders().getFirst("Content-Type"));
+ }
+
+ @Test
+ public void multiValueMap() {
+ MultiValueMap map = new LinkedMultiValueMap();
+ map.set("Content-Type", "text/plain");
+ HttpEntity entity = new HttpEntity("foo", map);
+ assertEquals(MediaType.TEXT_PLAIN, entity.getHeaders().getContentType());
+ assertEquals("text/plain", entity.getHeaders().getFirst("Content-Type"));
+ }
+
+ @Test
+ public void map() {
+ Map map = new LinkedHashMap();
+ map.put("Content-Type", "text/plain");
+ HttpEntity entity = new HttpEntity("foo", map);
+ assertEquals(MediaType.TEXT_PLAIN, entity.getHeaders().getContentType());
+ assertEquals("text/plain", entity.getHeaders().getFirst("Content-Type"));
+ }
+
+}
diff --git a/org.springframework.web/src/test/java/org/springframework/http/converter/FormHttpMessageConverterTests.java b/org.springframework.web/src/test/java/org/springframework/http/converter/FormHttpMessageConverterTests.java
index ab8999b380aa975d06fd91a53be93f7a42c369f2..55dd963447e5e95bdff00477dc80d8c74c1c00d0 100644
--- a/org.springframework.web/src/test/java/org/springframework/http/converter/FormHttpMessageConverterTests.java
+++ b/org.springframework.web/src/test/java/org/springframework/http/converter/FormHttpMessageConverterTests.java
@@ -36,6 +36,7 @@ import org.junit.Test;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
+import org.springframework.http.HttpEntity;
import org.springframework.http.MediaType;
import org.springframework.http.MockHttpInputMessage;
import org.springframework.http.MockHttpOutputMessage;
@@ -108,7 +109,8 @@ public class FormHttpMessageConverterTests {
Resource logo = new ClassPathResource("/org/springframework/http/converter/logo.jpg");
parts.add("logo", logo);
Source xml = new StreamSource(new StringReader(""));
- parts.add("xml", xml);
+ HttpEntity entity = new HttpEntity(xml, MediaType.TEXT_XML);
+ parts.add("xml", entity);
MockHttpOutputMessage outputMessage = new MockHttpOutputMessage();
converter.write(parts, MediaType.MULTIPART_FORM_DATA, outputMessage);
@@ -145,7 +147,7 @@ public class FormHttpMessageConverterTests {
item = (FileItem) items.get(4);
assertEquals("xml", item.getFieldName());
- assertEquals("application/xml", item.getContentType());
+ assertEquals("text/xml", item.getContentType());
}
private static class MockHttpOutputMessageRequestContext implements RequestContext {
diff --git a/org.springframework.web/src/test/java/org/springframework/web/client/RestTemplateIntegrationTests.java b/org.springframework.web/src/test/java/org/springframework/web/client/RestTemplateIntegrationTests.java
index e04604381a1adcf5566e52e6f4b63626cc976b4a..09054f116972d7e8093afb84b83109c2aec6f9d6 100644
--- a/org.springframework.web/src/test/java/org/springframework/web/client/RestTemplateIntegrationTests.java
+++ b/org.springframework.web/src/test/java/org/springframework/web/client/RestTemplateIntegrationTests.java
@@ -20,6 +20,8 @@ import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
+import java.nio.charset.Charset;
+import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;
@@ -47,7 +49,9 @@ import org.mortbay.jetty.servlet.ServletHolder;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
+import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
+import org.springframework.http.MediaType;
import org.springframework.http.client.CommonsClientHttpRequestFactory;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.LinkedMultiValueMap;
@@ -64,12 +68,14 @@ public class RestTemplateIntegrationTests {
private static final String URI = "http://localhost:8889";
+ private static MediaType contentType;
+
@BeforeClass
public static void startJettyServer() throws Exception {
jettyServer = new Server(8889);
Context jettyContext = new Context(jettyServer, "/");
byte[] bytes = helloWorld.getBytes("UTF-8");
- String contentType = "text/plain;charset=utf-8";
+ contentType = new MediaType("text", "plain", Collections.singletonMap("charset", "utf-8"));
jettyContext.addServlet(new ServletHolder(new GetServlet(bytes, contentType)), "/get");
jettyContext.addServlet(new ServletHolder(new GetServlet(new byte[0], contentType)), "/get/nothing");
jettyContext.addServlet(
@@ -100,6 +106,14 @@ public class RestTemplateIntegrationTests {
assertEquals("Invalid content", helloWorld, s);
}
+ @Test
+ public void getEntity() {
+ HttpEntity entity = template.getForEntity(URI + "/{method}", String.class, "get");
+ assertEquals("Invalid content", helloWorld, entity.getBody());
+ assertFalse("No headers", entity.getHeaders().isEmpty());
+ assertEquals("Invalid content-type", contentType, entity.getHeaders().getContentType());
+ }
+
@Test
public void getNoResponse() {
String s = template.getForObject(URI + "/get/nothing", String.class);
@@ -112,6 +126,13 @@ public class RestTemplateIntegrationTests {
assertEquals("Invalid location", new URI(URI + "/post/1"), location);
}
+ @Test
+ public void postForLocationEntity() throws URISyntaxException {
+ HttpEntity entity = new HttpEntity(helloWorld, new MediaType("text", "plain", Charset.forName("ISO-8859-15")));
+ URI location = template.postForLocation(URI + "/{method}", entity, "post");
+ assertEquals("Invalid location", new URI(URI + "/post/1"), location);
+ }
+
@Test
public void postForObject() throws URISyntaxException {
String s = template.postForObject(URI + "/{method}", helloWorld, String.class, "post");
@@ -156,6 +177,7 @@ public class RestTemplateIntegrationTests {
template.postForLocation(URI + "/multipart", parts);
}
+
/** Servlet that returns and error message for a given status code. */
private static class ErrorServlet extends GenericServlet {
@@ -175,9 +197,9 @@ public class RestTemplateIntegrationTests {
private final byte[] buf;
- private final String contentType;
+ private final MediaType contentType;
- private GetServlet(byte[] buf, String contentType) {
+ private GetServlet(byte[] buf, MediaType contentType) {
this.buf = buf;
this.contentType = contentType;
}
@@ -185,7 +207,7 @@ public class RestTemplateIntegrationTests {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
- response.setContentType(contentType);
+ response.setContentType(contentType.toString());
response.setContentLength(buf.length);
FileCopyUtils.copy(buf, response.getOutputStream());
}
@@ -199,9 +221,9 @@ public class RestTemplateIntegrationTests {
private final byte[] buf;
- private final String contentType;
+ private final MediaType contentType;
- private PostServlet(String s, String location, byte[] buf, String contentType) {
+ private PostServlet(String s, String location, byte[] buf, MediaType contentType) {
this.s = s;
this.location = location;
this.buf = buf;
@@ -218,7 +240,7 @@ public class RestTemplateIntegrationTests {
response.setStatus(HttpServletResponse.SC_CREATED);
response.setHeader("Location", location);
response.setContentLength(buf.length);
- response.setContentType(contentType);
+ response.setContentType(contentType.toString());
FileCopyUtils.copy(buf, response.getOutputStream());
}
}
diff --git a/org.springframework.web/src/test/java/org/springframework/web/client/RestTemplateTests.java b/org.springframework.web/src/test/java/org/springframework/web/client/RestTemplateTests.java
index 82038361e170963f0c31a7ba258159405dc9c075..87bf2bab9016b631d482e5db08e1231cedcbaf2e 100644
--- a/org.springframework.web/src/test/java/org/springframework/web/client/RestTemplateTests.java
+++ b/org.springframework.web/src/test/java/org/springframework/web/client/RestTemplateTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2009 the original author or authors.
+ * Copyright 2002-2010 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.
@@ -28,6 +28,7 @@ import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;
+import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
@@ -176,6 +177,36 @@ public class RestTemplateTests {
verifyMocks();
}
+
+ @Test
+ public void getForEntity() throws Exception {
+ expect(converter.canRead(String.class, null)).andReturn(true);
+ MediaType textPlain = new MediaType("text", "plain");
+ expect(converter.getSupportedMediaTypes()).andReturn(Collections.singletonList(textPlain));
+ expect(requestFactory.createRequest(new URI("http://example.com"), HttpMethod.GET)).andReturn(request);
+ HttpHeaders requestHeaders = new HttpHeaders();
+ expect(request.getHeaders()).andReturn(requestHeaders);
+ expect(request.execute()).andReturn(response);
+ expect(errorHandler.hasError(response)).andReturn(false);
+ HttpHeaders responseHeaders = new HttpHeaders();
+ responseHeaders.setContentType(textPlain);
+ expect(response.getHeaders()).andReturn(responseHeaders).times(2);
+ expect(converter.canRead(String.class, textPlain)).andReturn(true);
+ String expected = "Hello World";
+ expect(converter.read(String.class, response)).andReturn(expected);
+ response.close();
+
+ replayMocks();
+
+ HttpEntity result = template.getForEntity("http://example.com", String.class);
+ assertEquals("Invalid GET result", expected, result.getBody());
+ assertEquals("Invalid Accept header", textPlain.toString(), requestHeaders.getFirst("Accept"));
+ assertEquals("Invalid Content-Type header", textPlain, result.getHeaders().getContentType());
+
+ verifyMocks();
+ }
+
+
@Test
public void headForHeaders() throws Exception {
expect(requestFactory.createRequest(new URI("http://example.com"), HttpMethod.HEAD)).andReturn(request);
@@ -215,6 +246,60 @@ public class RestTemplateTests {
verifyMocks();
}
+ @Test
+ public void postForLocationEntityContentType() throws Exception {
+ expect(requestFactory.createRequest(new URI("http://example.com"), HttpMethod.POST)).andReturn(request);
+ String helloWorld = "Hello World";
+ MediaType contentType = MediaType.TEXT_PLAIN;
+ expect(converter.canWrite(String.class, contentType)).andReturn(true);
+ HttpHeaders requestHeaders = new HttpHeaders();
+ expect(request.getHeaders()).andReturn(requestHeaders);
+ converter.write(helloWorld, contentType, request);
+ expect(request.execute()).andReturn(response);
+ expect(errorHandler.hasError(response)).andReturn(false);
+ HttpHeaders responseHeaders = new HttpHeaders();
+ URI expected = new URI("http://example.com/hotels");
+ responseHeaders.setLocation(expected);
+ expect(response.getHeaders()).andReturn(responseHeaders);
+ response.close();
+
+ replayMocks();
+
+ HttpEntity entity = new HttpEntity(helloWorld, contentType);
+
+ URI result = template.postForLocation("http://example.com", entity);
+ assertEquals("Invalid POST result", expected, result);
+
+ verifyMocks();
+ }
+
+ @Test
+ public void postForLocationEntityCustomHeader() throws Exception {
+ expect(requestFactory.createRequest(new URI("http://example.com"), HttpMethod.POST)).andReturn(request);
+ String helloWorld = "Hello World";
+ expect(converter.canWrite(String.class, null)).andReturn(true);
+ HttpHeaders requestHeaders = new HttpHeaders();
+ expect(request.getHeaders()).andReturn(requestHeaders);
+ converter.write(helloWorld, null, request);
+ expect(request.execute()).andReturn(response);
+ expect(errorHandler.hasError(response)).andReturn(false);
+ HttpHeaders responseHeaders = new HttpHeaders();
+ URI expected = new URI("http://example.com/hotels");
+ responseHeaders.setLocation(expected);
+ expect(response.getHeaders()).andReturn(responseHeaders);
+ response.close();
+
+ replayMocks();
+
+ HttpEntity entity = new HttpEntity(helloWorld, Collections.singletonMap("MyHeader", "MyValue"));
+
+ URI result = template.postForLocation("http://example.com", entity);
+ assertEquals("Invalid POST result", expected, result);
+ assertEquals("No custom header set", "MyValue", requestHeaders.getFirst("MyHeader"));
+
+ verifyMocks();
+ }
+
@Test
public void postForLocationNoLocation() throws Exception {
expect(requestFactory.createRequest(new URI("http://example.com"), HttpMethod.POST)).andReturn(request);
@@ -283,6 +368,37 @@ public class RestTemplateTests {
verifyMocks();
}
+ @Test
+ public void postForEntity() throws Exception {
+ MediaType textPlain = new MediaType("text", "plain");
+ expect(converter.canRead(Integer.class, null)).andReturn(true);
+ expect(converter.getSupportedMediaTypes()).andReturn(Collections.singletonList(textPlain));
+ expect(requestFactory.createRequest(new URI("http://example.com"), HttpMethod.POST)).andReturn(this.request);
+ HttpHeaders requestHeaders = new HttpHeaders();
+ expect(this.request.getHeaders()).andReturn(requestHeaders);
+ String request = "Hello World";
+ expect(converter.canWrite(String.class, null)).andReturn(true);
+ converter.write(request, null, this.request);
+ expect(this.request.execute()).andReturn(response);
+ expect(errorHandler.hasError(response)).andReturn(false);
+ HttpHeaders responseHeaders = new HttpHeaders();
+ responseHeaders.setContentType(textPlain);
+ expect(response.getHeaders()).andReturn(responseHeaders).times(2);
+ Integer expected = 42;
+ expect(converter.canRead(Integer.class, textPlain)).andReturn(true);
+ expect(converter.read(Integer.class, response)).andReturn(expected);
+ response.close();
+
+ replayMocks();
+
+ HttpEntity result = template.postForEntity("http://example.com", request, Integer.class);
+ assertEquals("Invalid POST result", expected, result.getBody());
+ assertEquals("Invalid Content-Type", textPlain, result.getHeaders().getContentType());
+ assertEquals("Invalid Accept header", textPlain.toString(), requestHeaders.getFirst("Accept"));
+
+ verifyMocks();
+ }
+
@Test
public void postForObjectNull() throws Exception {
MediaType textPlain = new MediaType("text", "plain");
@@ -301,7 +417,34 @@ public class RestTemplateTests {
response.close();
replayMocks();
- template.postForObject("http://example.com", null, Integer.class);
+ Integer result = template.postForObject("http://example.com", null, Integer.class);
+ assertNull("Invalid POST result", result);
+ assertEquals("Invalid content length", 0, requestHeaders.getContentLength());
+
+ verifyMocks();
+ }
+
+ @Test
+ public void postForEntityNull() throws Exception {
+ MediaType textPlain = new MediaType("text", "plain");
+ expect(converter.canRead(Integer.class, null)).andReturn(true);
+ expect(converter.getSupportedMediaTypes()).andReturn(Collections.singletonList(textPlain));
+ expect(requestFactory.createRequest(new URI("http://example.com"), HttpMethod.POST)).andReturn(request);
+ HttpHeaders requestHeaders = new HttpHeaders();
+ expect(request.getHeaders()).andReturn(requestHeaders).times(2);
+ expect(request.execute()).andReturn(response);
+ expect(errorHandler.hasError(response)).andReturn(false);
+ HttpHeaders responseHeaders = new HttpHeaders();
+ responseHeaders.setContentType(textPlain);
+ expect(response.getHeaders()).andReturn(responseHeaders).times(2);
+ expect(converter.canRead(Integer.class, textPlain)).andReturn(true);
+ expect(converter.read(Integer.class, response)).andReturn(null);
+ response.close();
+
+ replayMocks();
+ HttpEntity result = template.postForEntity("http://example.com", null, Integer.class);
+ assertFalse("Invalid POST result", result.hasBody());
+ assertEquals("Invalid Content-Type", textPlain, result.getHeaders().getContentType());
assertEquals("Invalid content length", 0, requestHeaders.getContentLength());
verifyMocks();