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 e0722e63a2e..a5d14bd9f5a 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 @@ -17,7 +17,6 @@ package org.springframework.web.client; import java.net.URI; -import java.util.EnumSet; import java.util.Map; import java.util.Set; @@ -40,24 +39,22 @@ public interface RestOperations { /** * Retrieve a representation by doing a GET on the specified URL. *

URI Template variables are expanded using the given URI variables, if any. - * @param uri the URI + * @param uri the URI * @param responseType the type of the return value * @param uriVariables the variables to expand the template * @return the converted object */ - T getForObject(String uri, Class responseType, String... uriVariables) - throws RestClientException; + T getForObject(String uri, Class responseType, String... uriVariables) throws RestClientException; /** * Retrieve a representation by doing a GET on the URI template. *

URI Template variables are expanded using the given map. - * @param uri the URI + * @param uri the URI * @param responseType the type of the return value * @param uriVariables the map containing variables for the URI template * @return the converted object */ - T getForObject(String uri, Class responseType, Map uriVariables) - throws RestClientException; + T getForObject(String uri, Class responseType, Map uriVariables) throws RestClientException; // HEAD @@ -65,7 +62,7 @@ public interface RestOperations { /** * Retrieve all headers of the resource specified by the URI template. *

URI Template variables are expanded using the given URI variables, if any. - * @param uri the URI + * @param uri the URI * @param uriVariables the variables to expand the template * @return all HTTP headers of that resource */ @@ -74,7 +71,7 @@ public interface RestOperations { /** * Retrieve all headers of the resource specified by the URI template. *

URI Template variables are expanded using the given map. - * @param uri the URI + * @param uri the URI * @param uriVariables the map containing variables for the URI template * @return all HTTP headers of that resource */ @@ -84,27 +81,25 @@ public interface RestOperations { // POST /** - * Create a new resource by POSTing the given object to the URI template. The value of the Location, - * indicating where the new resource is stored, is returned. + * Create a new resource by POSTing the given object to the URI template. The value of the Location + * header, indicating where the new resource is stored, is returned. *

URI Template variables are expanded using the given URI variables, if any. - * @param uri the URI - * @param request the Object to be POSTED + * @param uri the URI + * @param request the Object to be POSTed, may be null * @return the value for the Location header */ - URI postForLocation(String uri, Object request, String... uriVariables) - throws RestClientException; + URI postForLocation(String uri, Object request, String... uriVariables) throws RestClientException; /** - * Create a new resource by POSTing the given object to URI template. The value of the Location, + * Create a new resource by POSTing the given object to URI template. The value of the Location header, * indicating where the new resource is stored, is returned. *

URI Template variables are expanded using the given map. - * @param uri the URI - * @param request the Object to be POSTed + * @param uri the URI + * @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 */ - URI postForLocation(String uri, Object request, Map uriVariables) - throws RestClientException; + URI postForLocation(String uri, Object request, Map uriVariables) throws RestClientException; // PUT @@ -112,8 +107,8 @@ public interface RestOperations { /** * 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. - * @param uri the URI - * @param request the Object to be POSTed + * @param uri the URI + * @param request the Object to be PUT, may be null * @param uriVariables the variables to expand the template */ void put(String uri, Object request, String... uriVariables) throws RestClientException; @@ -121,8 +116,8 @@ public interface RestOperations { /** * Creates a new resource by PUTting the given object to URI template. *

URI Template variables are expanded using the given map. - * @param uri the URI - * @param request the Object to be POSTed + * @param uri the URI + * @param request the Object to be PUT, may be null * @param uriVariables the variables to expand the template */ void put(String uri, Object request, Map uriVariables) throws RestClientException; @@ -133,7 +128,7 @@ public interface RestOperations { /** * Delete the resources at the specified URI. *

URI Template variables are expanded using the given URI variables, if any. - * @param uri the URI + * @param uri the URI * @param uriVariables the variables to expand in the template */ void delete(String uri, String... uriVariables) throws RestClientException; @@ -141,7 +136,7 @@ public interface RestOperations { /** * Delete the resources at the specified URI. *

URI Template variables are expanded using the given map. - * @param uri the URI + * @param uri the URI * @param uriVariables the variables to expand the template */ void delete(String uri, Map uriVariables) throws RestClientException; @@ -152,22 +147,20 @@ public interface RestOperations { /** * Return the value of the Allow header for the given URI. *

URI Template variables are expanded using the given URI variables, if any. - * @param uri the URI + * @param uri the URI * @param uriVariables the variables to expand in the template * @return the value of the allow header */ - Set optionsForAllow(String uri, String... uriVariables) - throws RestClientException; + Set optionsForAllow(String uri, String... uriVariables) throws RestClientException; /** * Return the value of the Allow header for the given URI. *

URI Template variables are expanded using the given map. - * @param uri the URI + * @param uri the URI * @param uriVariables the variables to expand in the template * @return the value of the allow header */ - Set optionsForAllow(String uri, Map uriVariables) - throws RestClientException; + Set optionsForAllow(String uri, Map uriVariables) throws RestClientException; // general execution @@ -176,30 +169,34 @@ public interface RestOperations { * Execute the HTTP methods to the given URI, preparing the request with the {@link RequestCallback}, * and reading the response with a {@link ResponseExtractor}. *

URI Template variables are expanded using the given URI variables, if any. - * @param uri the URI - * @param method the HTTP method (GET, POST, etc) - * @param requestCallback object that prepares the request + * @param uri the URI + * @param method the HTTP method (GET, POST, etc) + * @param requestCallback object that prepares the request * @param responseExtractor object that extracts the return value from the response - * @param uriVariables the variables to expand in the template + * @param uriVariables the variables to expand in the template * @return an arbitrary object, as returned by the {@link ResponseExtractor} */ - T execute(String uri, HttpMethod method, RequestCallback requestCallback, - ResponseExtractor responseExtractor, String... uriVariables) - throws RestClientException; + T execute(String uri, + HttpMethod method, + RequestCallback requestCallback, + ResponseExtractor responseExtractor, + String... uriVariables) throws RestClientException; /** * Execute the HTTP methods to the given URI, preparing the request with the {@link RequestCallback}, * and reading the response with a {@link ResponseExtractor}. *

URI Template variables are expanded using the given URI variables map. - * @param uri the URI - * @param method the HTTP method (GET, POST, etc) - * @param requestCallback object that prepares the request + * @param uri the URI + * @param method the HTTP method (GET, POST, etc) + * @param requestCallback object that prepares the request * @param responseExtractor object that extracts the return value from the response - * @param uriVariablesthe variables to expand in the template + * @param uriVariables the variables to expand in the template * @return an arbitrary object, as returned by the {@link ResponseExtractor} */ - T execute(String uri, HttpMethod method, RequestCallback requestCallback, - ResponseExtractor responseExtractor, Map uriVariables) - throws RestClientException; + T execute(String uri, + HttpMethod method, + RequestCallback requestCallback, + ResponseExtractor responseExtractor, + Map uriVariables) 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 b0f36aacdfc..b4912f5e9fe 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 @@ -91,14 +91,13 @@ public class RestTemplate extends HttpAccessor implements RestOperations { private final ResponseExtractor headersExtractor = new HeadersExtractor(); private HttpMessageConverter[] messageConverters = - new HttpMessageConverter[] {new ByteArrayHttpMessageConverter(), new StringHttpMessageConverter()}; + new HttpMessageConverter[]{new ByteArrayHttpMessageConverter(), new StringHttpMessageConverter()}; private ResponseErrorHandler errorHandler = new DefaultResponseErrorHandler(); /** * Create a new instance of the {@link RestTemplate} using default settings. - * @see #initDefaultStrategies() */ public RestTemplate() { } @@ -166,8 +165,7 @@ public class RestTemplate extends HttpAccessor implements RestOperations { // GET - public T getForObject(String url, Class responseType, String... urlVariables) - throws RestClientException { + public T getForObject(String url, Class responseType, String... urlVariables) throws RestClientException { checkForSupportedMessageConverter(responseType); return execute(url, HttpMethod.GET, new GetCallback(responseType), @@ -196,10 +194,10 @@ public class RestTemplate extends HttpAccessor implements RestOperations { // POST - public URI postForLocation(String url, Object request, String... urlVariables) - throws RestClientException { - - checkForSupportedMessageConverter(request.getClass()); + public URI postForLocation(String url, Object request, String... urlVariables) throws RestClientException { + if (request != null) { + checkForSupportedMessageConverter(request.getClass()); + } HttpHeaders headers = execute(url, HttpMethod.POST, new PostPutCallback(request), this.headersExtractor, urlVariables); return headers.getLocation(); @@ -207,8 +205,9 @@ public class RestTemplate extends HttpAccessor implements RestOperations { public URI postForLocation(String url, Object request, Map urlVariables) throws RestClientException { - - checkForSupportedMessageConverter(request.getClass()); + if (request != null) { + checkForSupportedMessageConverter(request.getClass()); + } HttpHeaders headers = execute(url, HttpMethod.POST, new PostPutCallback(request), this.headersExtractor, urlVariables); return headers.getLocation(); @@ -218,12 +217,16 @@ public class RestTemplate extends HttpAccessor implements RestOperations { // PUT public void put(String url, Object request, String... urlVariables) throws RestClientException { - checkForSupportedMessageConverter(request.getClass()); + if (request != null) { + checkForSupportedMessageConverter(request.getClass()); + } execute(url, HttpMethod.PUT, new PostPutCallback(request), null, urlVariables); } public void put(String url, Object request, Map urlVariables) throws RestClientException { - checkForSupportedMessageConverter(request.getClass()); + if (request != null) { + checkForSupportedMessageConverter(request.getClass()); + } execute(url, HttpMethod.PUT, new PostPutCallback(request), null, urlVariables); } @@ -241,15 +244,13 @@ public class RestTemplate extends HttpAccessor implements RestOperations { // OPTIONS - public Set optionsForAllow(String url, String... urlVariables) - throws RestClientException { + public Set optionsForAllow(String url, String... urlVariables) throws RestClientException { HttpHeaders headers = execute(url, HttpMethod.OPTIONS, null, this.headersExtractor, urlVariables); return headers.getAllow(); } - public Set optionsForAllow(String url, Map urlVariables) - throws RestClientException { + public Set optionsForAllow(String url, Map urlVariables) throws RestClientException { HttpHeaders headers = execute(url, HttpMethod.OPTIONS, null, this.headersExtractor, urlVariables); return headers.getAllow(); @@ -258,18 +259,22 @@ public class RestTemplate extends HttpAccessor implements RestOperations { // general execution - public T execute(String url, HttpMethod method, RequestCallback requestCallback, - ResponseExtractor responseExtractor, String... urlVariables) - throws RestClientException { + public T execute(String url, + HttpMethod method, + RequestCallback requestCallback, + ResponseExtractor responseExtractor, + String... urlVariables) throws RestClientException { UriTemplate uriTemplate = new UriTemplate(url); URI expanded = uriTemplate.expand(urlVariables); return doExecute(expanded, method, requestCallback, responseExtractor); } - public T execute(String url,HttpMethod method, RequestCallback requestCallback, - ResponseExtractor responseExtractor, Map urlVariables) - throws RestClientException { + public T execute(String url, + HttpMethod method, + RequestCallback requestCallback, + ResponseExtractor responseExtractor, + Map urlVariables) throws RestClientException { UriTemplate uriTemplate = new UriTemplate(url); URI expanded = uriTemplate.expand(urlVariables); @@ -279,13 +284,15 @@ public class RestTemplate extends HttpAccessor implements RestOperations { /** * Execute the given method on the provided URI. The {@link ClientHttpRequest} is processed using the {@link * RequestCallback}; the response with the {@link ResponseExtractor}. - * @param url the fully-expanded URL to connect to - * @param method the HTTP method to execute (GET, POST, etc.) - * @param requestCallback object that prepares the request (can be null) + * @param url the fully-expanded URL to connect to + * @param method the HTTP method to execute (GET, POST, etc.) + * @param requestCallback object that prepares the request (can be null) * @param responseExtractor object that extracts the return value from the response (can be null) * @return an arbitrary object, as returned by the {@link ResponseExtractor} */ - protected T doExecute(URI url, HttpMethod method, RequestCallback requestCallback, + protected T doExecute(URI url, + HttpMethod method, + RequestCallback requestCallback, ResponseExtractor responseExtractor) throws RestClientException { Assert.notNull(url, "'url' must not be null"); @@ -376,12 +383,16 @@ public class RestTemplate extends HttpAccessor implements RestOperations { @SuppressWarnings("unchecked") public void doWithRequest(ClientHttpRequest httpRequest) throws IOException { - HttpMessageConverter entityConverter = getSupportedMessageConverters(this.request.getClass()).get(0); - entityConverter.write(this.request, httpRequest); + if (request != null) { + HttpMessageConverter entityConverter = getSupportedMessageConverters(this.request.getClass()).get(0); + entityConverter.write(this.request, httpRequest); + } + else { + httpRequest.getHeaders().setContentLength(0L); + } } } - /** * Response extractor that uses the registered {@linkplain HttpMessageConverter entity converters} * to convert the response into a type T. 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 06ed125d9b4..813d932eee7 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 @@ -263,6 +263,24 @@ public class RestTemplateTests { verifyMocks(); } + @Test + public void postNull() throws Exception { + expect(requestFactory.createRequest(new URI("http://example.com"), HttpMethod.POST)).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(); + expect(response.getHeaders()).andReturn(responseHeaders); + response.close(); + + replayMocks(); + template.postForLocation("http://example.com", null); + assertEquals("Invalid content length", 0, requestHeaders.getContentLength()); + + verifyMocks(); + } + @Test public void put() throws Exception { expect(converter.supports(String.class)).andReturn(true).times(2); @@ -280,6 +298,22 @@ public class RestTemplateTests { verifyMocks(); } + @Test + public void putNull() throws Exception { + expect(requestFactory.createRequest(new URI("http://example.com"), HttpMethod.PUT)).andReturn(request); + HttpHeaders requestHeaders = new HttpHeaders(); + expect(request.getHeaders()).andReturn(requestHeaders); + expect(request.execute()).andReturn(response); + expect(errorHandler.hasError(response)).andReturn(false); + response.close(); + + replayMocks(); + template.put("http://example.com", null); + assertEquals("Invalid content length", 0, requestHeaders.getContentLength()); + + verifyMocks(); + } + @Test public void delete() throws Exception { expect(requestFactory.createRequest(new URI("http://example.com"), HttpMethod.DELETE)).andReturn(request);