提交 63076d08 编写于 作者: A Arjen Poutsma

SPR-6969 - Introduce HttpEntity

上级 fc0a6ce4
/*
* 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.
*
* <p>Typically used in combination with the {@link org.springframework.web.client.RestTemplate RestTemplate}, like so:
* <pre class="code">
* HttpEntity&lt;String&gt; entity = new HttpEntity&lt;String&gt;(helloWorld, MediaType.TEXT_PLAIN);
* URI location = template.postForLocation("http://example.com", entity);
* </pre>
* or
* <pre class="code">
* HttpEntity&lt;String&gt; entity = template.getForEntity("http://example.com", String.class);
* String body = entity.getBody();
* MediaType contentType = entity.getHeaders().getContentType();
* </pre>
*
* @author Arjen Poutsma
* @see org.springframework.web.client.RestTemplate
* @see #getBody()
* @see #getHeaders()
* @since 3.0.2
*/
public class HttpEntity<T> {
/**
* 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<String, String>) 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<String, String>) null);
}
/**
* Create a new {@code HttpEntity} with the given headers and no body.
*
* @param headers the entity headers
*/
public HttpEntity(Map<String, String> 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<String, String> 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<String, String> 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<String, String> headers) {
this.body = body;
HttpHeaders tempHeaders = new HttpHeaders();
if (headers != null) {
tempHeaders.putAll(headers);
}
this.headers = HttpHeaders.readOnlyHttpHeaders(tempHeaders);
}
private static MultiValueMap<String, String> toMultiValueMap(Map<String, String> map) {
if (map == null) {
return null;
}
else {
MultiValueMap<String, String> result = new LinkedMultiValueMap<String, String>(map.size());
result.setAll(map);
return result;
}
}
private static MultiValueMap<String, String> 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;
}
}
......@@ -162,15 +162,19 @@ public abstract class AbstractHttpMessageConverter<T> 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();
......
......@@ -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<MultiValue
String name = entry.getKey();
for (Object part : entry.getValue()) {
writeBoundary(boundary, os);
writePart(name, part, os);
HttpEntity entity = getEntity(part);
writePart(name, entity, os);
writeNewLine(os);
}
}
......@@ -255,13 +257,29 @@ public class FormHttpMessageConverter implements HttpMessageConverter<MultiValue
}
@SuppressWarnings("unchecked")
private void writePart(String name, Object part, OutputStream os) throws IOException {
Class<?> 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;
}
}
......
/*
* 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> T getForObject(URI url, Class<T> responseType) throws RestClientException;
/**
* Retrieve an entity by doing a GET on the specified URL.
* The response is converted and stored in an {@link HttpEntity}.
* <p>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
*/
<T> HttpEntity<T> getForEntity(String url, Class<T> 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}.
* <p>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
*/
<T> HttpEntity<T> getForEntity(String url, Class<T> responseType, Map<String, ?> 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
*/
<T> HttpEntity<T> getForEntity(URI url, Class<T> 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
* <code>Location</code> header. This header typically indicates where the new resource is stored.
* <p>URI Template variables are expanded using the given URI variables, if any.
* <p>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 <code>null</code>
* @param uriVariables the variables to expand the template
* @return the value for the <code>Location</code> 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
* <code>Location</code> header. This header typically indicates where the new resource is stored.
* <p>URI Template variables are expanded using the given map.
* <p>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 <code>null</code>
* @param uriVariables the variables to expand the template
* @return the value for the <code>Location</code> header
* @see HttpEntity
*/
URI postForLocation(String url, Object request, Map<String, ?> uriVariables) throws RestClientException;
/**
* Create a new resource by POSTing the given object to the URL, and returns the value of the
* <code>Location</code> header. This header typically indicates where the new resource is stored.
* <p>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 <code>null</code>
* @return the value for the <code>Location</code> 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.
* <p>URI Template variables are expanded using the given URI variables, if any.
* <p>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 <code>null</code>
* @param uriVariables the variables to expand the template
* @return the converted object
* @see HttpEntity
*/
<T> T postForObject(String url, Object request, Class<T> 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.
* <p>URI Template variables are expanded using the given map.
* <p>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 <code>null</code>
* @param uriVariables the variables to expand the template
* @return the converted object
* @see HttpEntity
*/
<T> T postForObject(String url, Object request, Class<T> responseType, Map<String, ?> 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.
* <p>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 <code>null</code>
* @return the converted object
* @see HttpEntity
*/
<T> T postForObject(URI url, Object request, Class<T> responseType) throws RestClientException;
/**
* Create a new resource by POSTing the given object to the URI template,
* and returns the response as {@link HttpEntity}.
* <p>URI Template variables are expanded using the given URI variables, if any.
* <p>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 <code>null</code>
* @param uriVariables the variables to expand the template
* @return the converted object
* @see HttpEntity
* @since 3.0.2
*/
<T> HttpEntity<T> postForEntity(String url, Object request, Class<T> 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}.
* <p>URI Template variables are expanded using the given map.
* <p>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 <code>null</code>
* @param uriVariables the variables to expand the template
* @return the converted object
* @see HttpEntity
* @since 3.0.2
*/
<T> HttpEntity<T> postForEntity(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables)
throws RestClientException;
/**
* Create a new resource by POSTing the given object to the URL,
* and returns the response as {@link HttpEntity}.
* <p>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 <code>null</code>
* @return the converted object
* @see HttpEntity
* @since 3.0.2
*/
<T> HttpEntity<T> postForEntity(URI url, Object request, Class<T> responseType) throws RestClientException;
// PUT
/**
* Create or update a resource by PUTting the given object to the URI.
* <p>URI Template variables are expanded using the given URI variables, if any.
* <p>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 <code>null</code>
* @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.
* <p>URI Template variables are expanded using the given map.
* <p>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 <code>null</code>
* @param uriVariables the variables to expand the template
* @see HttpEntity
*/
void put(String url, Object request, Map<String, ?> uriVariables) throws RestClientException;
/**
* Creates a new resource by PUTting the given object to URL.
* <p>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 <code>null</code>
* @see HttpEntity
*/
void put(URI url, Object request) throws RestClientException;
......
......@@ -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> T getForObject(String url, Class<T> responseType, Object... urlVariables) throws RestClientException {
AcceptHeaderRequestCallback<T> requestCallback = new AcceptHeaderRequestCallback<T>(responseType);
AcceptHeaderRequestCallback requestCallback = new AcceptHeaderRequestCallback(responseType);
HttpMessageConverterExtractor<T> responseExtractor =
new HttpMessageConverterExtractor<T>(responseType, getMessageConverters());
return execute(url, HttpMethod.GET, requestCallback, responseExtractor, urlVariables);
}
public <T> T getForObject(String url, Class<T> responseType, Map<String, ?> urlVariables) throws RestClientException {
AcceptHeaderRequestCallback<T> requestCallback = new AcceptHeaderRequestCallback<T>(responseType);
AcceptHeaderRequestCallback requestCallback = new AcceptHeaderRequestCallback(responseType);
HttpMessageConverterExtractor<T> responseExtractor =
new HttpMessageConverterExtractor<T>(responseType, getMessageConverters());
return execute(url, HttpMethod.GET, requestCallback, responseExtractor, urlVariables);
}
public <T> T getForObject(URI url, Class<T> responseType) throws RestClientException {
AcceptHeaderRequestCallback<T> requestCallback = new AcceptHeaderRequestCallback<T>(responseType);
AcceptHeaderRequestCallback requestCallback = new AcceptHeaderRequestCallback(responseType);
HttpMessageConverterExtractor<T> responseExtractor =
new HttpMessageConverterExtractor<T>(responseType, getMessageConverters());
return execute(url, HttpMethod.GET, requestCallback, responseExtractor);
}
public <T> HttpEntity<T> getForEntity(String url, Class<T> responseType, Object... urlVariables)
throws RestClientException {
AcceptHeaderRequestCallback requestCallback = new AcceptHeaderRequestCallback(responseType);
HttpEntityResponseExtractor<T> responseExtractor =
new HttpEntityResponseExtractor<T>(responseType);
return execute(url, HttpMethod.GET, requestCallback, responseExtractor, urlVariables);
}
public <T> HttpEntity<T> getForEntity(String url, Class<T> responseType, Map<String, ?> urlVariables)
throws RestClientException {
AcceptHeaderRequestCallback requestCallback = new AcceptHeaderRequestCallback(responseType);
HttpEntityResponseExtractor<T> responseExtractor =
new HttpEntityResponseExtractor<T>(responseType);
return execute(url, HttpMethod.GET, requestCallback, responseExtractor, urlVariables);
}
public <T> HttpEntity<T> getForEntity(URI url, Class<T> responseType) throws RestClientException {
AcceptHeaderRequestCallback requestCallback = new AcceptHeaderRequestCallback(responseType);
HttpEntityResponseExtractor<T> responseExtractor =
new HttpEntityResponseExtractor<T>(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<String, ?> 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> T postForObject(String url, Object request, Class<T> responseType, Object... uriVariables)
throws RestClientException {
PostPutCallback<T> requestCallback = new PostPutCallback<T>(request, responseType);
HttpEntityRequestCallback requestCallback = new HttpEntityRequestCallback(request, responseType);
HttpMessageConverterExtractor<T> responseExtractor =
new HttpMessageConverterExtractor<T>(responseType, getMessageConverters());
return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables);
......@@ -249,33 +273,59 @@ public class RestTemplate extends HttpAccessor implements RestOperations {
public <T> T postForObject(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables)
throws RestClientException {
PostPutCallback<T> requestCallback = new PostPutCallback<T>(request, responseType);
HttpEntityRequestCallback requestCallback = new HttpEntityRequestCallback(request, responseType);
HttpMessageConverterExtractor<T> responseExtractor =
new HttpMessageConverterExtractor<T>(responseType, getMessageConverters());
return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables);
}
public <T> T postForObject(URI url, Object request, Class<T> responseType) throws RestClientException {
PostPutCallback<T> requestCallback = new PostPutCallback<T>(request, responseType);
HttpEntityRequestCallback requestCallback = new HttpEntityRequestCallback(request, responseType);
HttpMessageConverterExtractor<T> responseExtractor =
new HttpMessageConverterExtractor<T>(responseType, getMessageConverters());
return execute(url, HttpMethod.POST, requestCallback, responseExtractor);
}
public <T> HttpEntity<T> postForEntity(String url, Object request, Class<T> responseType, Object... uriVariables)
throws RestClientException {
HttpEntityRequestCallback requestCallback = new HttpEntityRequestCallback(request, responseType);
HttpEntityResponseExtractor<T> responseExtractor =
new HttpEntityResponseExtractor<T>(responseType);
return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables);
}
public <T> HttpEntity<T> postForEntity(String url,
Object request,
Class<T> responseType,
Map<String, ?> uriVariables)
throws RestClientException {
HttpEntityRequestCallback requestCallback = new HttpEntityRequestCallback(request, responseType);
HttpEntityResponseExtractor<T> responseExtractor =
new HttpEntityResponseExtractor<T>(responseType);
return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables);
}
public <T> HttpEntity<T> postForEntity(URI url, Object request, Class<T> responseType) throws RestClientException {
HttpEntityRequestCallback requestCallback = new HttpEntityRequestCallback(request, responseType);
HttpEntityResponseExtractor<T> responseExtractor =
new HttpEntityResponseExtractor<T>(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<String, ?> 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<T> implements RequestCallback {
private final Class<T> responseType;
private class AcceptHeaderRequestCallback implements RequestCallback {
private AcceptHeaderRequestCallback() {
responseType = null;
}
private final Class<?> responseType;
private AcceptHeaderRequestCallback(Class<T> 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<T> extends AcceptHeaderRequestCallback<T> {
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<T> 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<T> implements ResponseExtractor<HttpEntity<T>> {
private final HttpMessageConverterExtractor<T> delegate;
public HttpEntityResponseExtractor(Class<T> responseType) {
this.delegate = new HttpMessageConverterExtractor<T>(responseType, getMessageConverters());
}
public HttpEntity<T> extractData(ClientHttpResponse response) throws IOException {
T body = delegate.extractData(response);
return new HttpEntity<T>(body, response.getHeaders());
}
}
......
/*
* 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<String> entity = new HttpEntity<String>(body);
assertSame(body, entity.getBody());
assertTrue(entity.getHeaders().isEmpty());
}
@Test
public void contentType() {
MediaType contentType = MediaType.TEXT_PLAIN;
HttpEntity<String> entity = new HttpEntity<String>("foo", contentType);
assertEquals(contentType, entity.getHeaders().getContentType());
assertEquals("text/plain", entity.getHeaders().getFirst("Content-Type"));
}
@Test
public void multiValueMap() {
MultiValueMap<String, String> map = new LinkedMultiValueMap<String, String>();
map.set("Content-Type", "text/plain");
HttpEntity<String> entity = new HttpEntity<String>("foo", map);
assertEquals(MediaType.TEXT_PLAIN, entity.getHeaders().getContentType());
assertEquals("text/plain", entity.getHeaders().getFirst("Content-Type"));
}
@Test
public void map() {
Map<String, String> map = new LinkedHashMap<String, String>();
map.put("Content-Type", "text/plain");
HttpEntity<String> entity = new HttpEntity<String>("foo", map);
assertEquals(MediaType.TEXT_PLAIN, entity.getHeaders().getContentType());
assertEquals("text/plain", entity.getHeaders().getFirst("Content-Type"));
}
}
......@@ -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("<root><child/></root>"));
parts.add("xml", xml);
HttpEntity<Source> entity = new HttpEntity<Source>(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 {
......
......@@ -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<String> 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<String> entity = new HttpEntity<String>(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());
}
}
......
/*
* 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<String> 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<String> entity = new HttpEntity<String>(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<String> entity = new HttpEntity<String>(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<Integer> 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<Integer> 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();
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册