Browse Source

SPR-5515: NPE when passing null as a request to RestTemplate.postForLocation

pull/23217/head
Arjen Poutsma 17 years ago
parent
commit
4c0edc2b9d
  1. 89
      org.springframework.web/src/main/java/org/springframework/web/client/RestOperations.java
  2. 69
      org.springframework.web/src/main/java/org/springframework/web/client/RestTemplate.java
  3. 34
      org.springframework.web/src/test/java/org/springframework/web/client/RestTemplateTests.java

89
org.springframework.web/src/main/java/org/springframework/web/client/RestOperations.java

@ -17,7 +17,6 @@ @@ -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 { @@ -40,24 +39,22 @@ public interface RestOperations {
/**
* Retrieve a representation by doing a GET on the specified URL.
* <p>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> T getForObject(String uri, Class<T> responseType, String... uriVariables)
throws RestClientException;
<T> T getForObject(String uri, Class<T> responseType, String... uriVariables) throws RestClientException;
/**
* Retrieve a representation by doing a GET on the URI template.
* <p>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> T getForObject(String uri, Class<T> responseType, Map<String, String> uriVariables)
throws RestClientException;
<T> T getForObject(String uri, Class<T> responseType, Map<String, String> uriVariables) throws RestClientException;
// HEAD
@ -65,7 +62,7 @@ public interface RestOperations { @@ -65,7 +62,7 @@ public interface RestOperations {
/**
* Retrieve all headers of the resource specified by the URI template.
* <p>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 { @@ -74,7 +71,7 @@ public interface RestOperations {
/**
* Retrieve all headers of the resource specified by the URI template.
* <p>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 { @@ -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 <code>Location</code>,
* 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 <code>Location</code>
* header, indicating where the new resource is stored, is returned.
* <p>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 <code>null</code>
* @return the value for the <code>Location</code> 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 <code>Location</code>,
* Create a new resource by POSTing the given object to URI template. The value of the <code>Location</code> header,
* indicating where the new resource is stored, is returned.
* <p>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 <code>null</code>
* @param uriVariables the variables to expand the template
* @return the value for the <code>Location</code> header
*/
URI postForLocation(String uri, Object request, Map<String, String> uriVariables)
throws RestClientException;
URI postForLocation(String uri, Object request, Map<String, String> uriVariables) throws RestClientException;
// PUT
@ -112,8 +107,8 @@ public interface RestOperations { @@ -112,8 +107,8 @@ public interface RestOperations {
/**
* 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.
* @param uri the URI
* @param request the Object to be POSTed
* @param uri the URI
* @param request the Object to be PUT, may be <code>null</code>
* @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 { @@ -121,8 +116,8 @@ public interface RestOperations {
/**
* Creates a new resource by PUTting the given object to URI template.
* <p>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 <code>null</code>
* @param uriVariables the variables to expand the template
*/
void put(String uri, Object request, Map<String, String> uriVariables) throws RestClientException;
@ -133,7 +128,7 @@ public interface RestOperations { @@ -133,7 +128,7 @@ public interface RestOperations {
/**
* Delete the resources at the specified URI.
* <p>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 { @@ -141,7 +136,7 @@ public interface RestOperations {
/**
* Delete the resources at the specified URI.
* <p>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<String, String> uriVariables) throws RestClientException;
@ -152,22 +147,20 @@ public interface RestOperations { @@ -152,22 +147,20 @@ public interface RestOperations {
/**
* Return the value of the Allow header for the given URI.
* <p>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<HttpMethod> optionsForAllow(String uri, String... uriVariables)
throws RestClientException;
Set<HttpMethod> optionsForAllow(String uri, String... uriVariables) throws RestClientException;
/**
* Return the value of the Allow header for the given URI.
* <p>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<HttpMethod> optionsForAllow(String uri, Map<String, String> uriVariables)
throws RestClientException;
Set<HttpMethod> optionsForAllow(String uri, Map<String, String> uriVariables) throws RestClientException;
// general execution
@ -176,30 +169,34 @@ public interface RestOperations { @@ -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}.
* <p>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> T execute(String uri, HttpMethod method, RequestCallback requestCallback,
ResponseExtractor<T> responseExtractor, String... uriVariables)
throws RestClientException;
<T> T execute(String uri,
HttpMethod method,
RequestCallback requestCallback,
ResponseExtractor<T> 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}.
* <p>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> T execute(String uri, HttpMethod method, RequestCallback requestCallback,
ResponseExtractor<T> responseExtractor, Map<String, String> uriVariables)
throws RestClientException;
<T> T execute(String uri,
HttpMethod method,
RequestCallback requestCallback,
ResponseExtractor<T> responseExtractor,
Map<String, String> uriVariables) throws RestClientException;
}

69
org.springframework.web/src/main/java/org/springframework/web/client/RestTemplate.java

@ -91,14 +91,13 @@ public class RestTemplate extends HttpAccessor implements RestOperations { @@ -91,14 +91,13 @@ public class RestTemplate extends HttpAccessor implements RestOperations {
private final ResponseExtractor<HttpHeaders> 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 { @@ -166,8 +165,7 @@ public class RestTemplate extends HttpAccessor implements RestOperations {
// GET
public <T> T getForObject(String url, Class<T> responseType, String... urlVariables)
throws RestClientException {
public <T> T getForObject(String url, Class<T> responseType, String... urlVariables) throws RestClientException {
checkForSupportedMessageConverter(responseType);
return execute(url, HttpMethod.GET, new GetCallback<T>(responseType),
@ -196,10 +194,10 @@ public class RestTemplate extends HttpAccessor implements RestOperations { @@ -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 { @@ -207,8 +205,9 @@ public class RestTemplate extends HttpAccessor implements RestOperations {
public URI postForLocation(String url, Object request, Map<String, String> 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 { @@ -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<String, String> 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 { @@ -241,15 +244,13 @@ public class RestTemplate extends HttpAccessor implements RestOperations {
// OPTIONS
public Set<HttpMethod> optionsForAllow(String url, String... urlVariables)
throws RestClientException {
public Set<HttpMethod> optionsForAllow(String url, String... urlVariables) throws RestClientException {
HttpHeaders headers = execute(url, HttpMethod.OPTIONS, null, this.headersExtractor, urlVariables);
return headers.getAllow();
}
public Set<HttpMethod> optionsForAllow(String url, Map<String, String> urlVariables)
throws RestClientException {
public Set<HttpMethod> optionsForAllow(String url, Map<String, String> 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 { @@ -258,18 +259,22 @@ public class RestTemplate extends HttpAccessor implements RestOperations {
// general execution
public <T> T execute(String url, HttpMethod method, RequestCallback requestCallback,
ResponseExtractor<T> responseExtractor, String... urlVariables)
throws RestClientException {
public <T> T execute(String url,
HttpMethod method,
RequestCallback requestCallback,
ResponseExtractor<T> responseExtractor,
String... urlVariables) throws RestClientException {
UriTemplate uriTemplate = new UriTemplate(url);
URI expanded = uriTemplate.expand(urlVariables);
return doExecute(expanded, method, requestCallback, responseExtractor);
}
public <T> T execute(String url,HttpMethod method, RequestCallback requestCallback,
ResponseExtractor<T> responseExtractor, Map<String, String> urlVariables)
throws RestClientException {
public <T> T execute(String url,
HttpMethod method,
RequestCallback requestCallback,
ResponseExtractor<T> responseExtractor,
Map<String, String> urlVariables) throws RestClientException {
UriTemplate uriTemplate = new UriTemplate(url);
URI expanded = uriTemplate.expand(urlVariables);
@ -279,13 +284,15 @@ public class RestTemplate extends HttpAccessor implements RestOperations { @@ -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 <code>null</code>)
* @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 <code>null</code>)
* @param responseExtractor object that extracts the return value from the response (can be <code>null</code>)
* @return an arbitrary object, as returned by the {@link ResponseExtractor}
*/
protected <T> T doExecute(URI url, HttpMethod method, RequestCallback requestCallback,
protected <T> T doExecute(URI url,
HttpMethod method,
RequestCallback requestCallback,
ResponseExtractor<T> responseExtractor) throws RestClientException {
Assert.notNull(url, "'url' must not be null");
@ -376,12 +383,16 @@ public class RestTemplate extends HttpAccessor implements RestOperations { @@ -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 <code>T</code>.

34
org.springframework.web/src/test/java/org/springframework/web/client/RestTemplateTests.java

@ -263,6 +263,24 @@ public class RestTemplateTests { @@ -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 { @@ -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);

Loading…
Cancel
Save