Add PATCH method operation to RestTemplate

This commit adds a HTTP PATCH operation to the RestTemplate:
patchForObject. As with most operations, there are three variants:
varargs, Map, and URI based.

Issue: SPR-14857
This commit is contained in:
Arjen Poutsma
2016-11-17 10:14:44 +01:00
parent f0ceefba0e
commit dbe81bef52
5 changed files with 208 additions and 6 deletions

View File

@@ -44,7 +44,10 @@ import org.junit.BeforeClass;
import org.springframework.http.MediaType;
import org.springframework.util.FileCopyUtils;
import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
/**
* @author Arjen Poutsma
@@ -92,6 +95,8 @@ public class AbstractJettyServerTestCase {
handler.addServlet(new ServletHolder(new MultipartServlet()), "/multipart");
handler.addServlet(new ServletHolder(new FormServlet()), "/form");
handler.addServlet(new ServletHolder(new DeleteServlet()), "/delete");
handler.addServlet(new ServletHolder(new PatchServlet(helloWorld, bytes, textContentType)),
"/patch");
handler.addServlet(
new ServletHolder(new PutServlet(helloWorld, bytes, textContentType)),
"/put");
@@ -333,4 +338,36 @@ public class AbstractJettyServerTestCase {
}
}
@SuppressWarnings("serial")
private static class PatchServlet extends GenericServlet {
private final String content;
private final byte[] buf;
private final MediaType contentType;
public PatchServlet(String content, byte[] buf, MediaType contentType) {
this.content = content;
this.buf = buf;
this.contentType = contentType;
}
@Override
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
assertEquals("PATCH", request.getMethod());
assertTrue("Invalid request content-length", request.getContentLength() > 0);
assertNotNull("No content-type", request.getContentType());
String body = FileCopyUtils.copyToString(request.getReader());
assertEquals("Invalid request body", content, body);
response.setStatus(HttpServletResponse.SC_CREATED);
response.setContentLength(buf.length);
response.setContentType(contentType.toString());
FileCopyUtils.copy(buf, response.getOutputStream());
}
}
}

View File

@@ -45,7 +45,13 @@ import org.springframework.http.converter.json.MappingJacksonValue;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import static org.junit.Assert.*;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
* @author Arjen Poutsma
@@ -123,6 +129,12 @@ public class RestTemplateIntegrationTests extends AbstractJettyServerTestCase {
assertEquals("Invalid content", helloWorld, s);
}
@Test
public void patchForObject() throws URISyntaxException {
String s = template.patchForObject(baseUrl + "/{method}", helloWorld, String.class, "patch");
assertEquals("Invalid content", helloWorld, s);
}
@Test
public void notFound() {
try {

View File

@@ -44,8 +44,17 @@ import org.springframework.http.converter.GenericHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.util.DefaultUriTemplateHandler;
import static org.junit.Assert.*;
import static org.mockito.BDDMockito.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.fail;
import static org.mockito.BDDMockito.any;
import static org.mockito.BDDMockito.eq;
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.mock;
import static org.mockito.BDDMockito.verify;
import static org.mockito.BDDMockito.willThrow;
/**
* @author Arjen Poutsma
@@ -590,6 +599,69 @@ public class RestTemplateTests {
verify(response).close();
}
@Test
public void patchForObject() throws Exception {
MediaType textPlain = new MediaType("text", "plain");
given(converter.canRead(Integer.class, null)).willReturn(true);
given(converter.getSupportedMediaTypes()).willReturn(Collections.singletonList(textPlain));
given(requestFactory.createRequest(new URI("http://example.com"), HttpMethod.PATCH)).willReturn(this.request);
HttpHeaders requestHeaders = new HttpHeaders();
given(this.request.getHeaders()).willReturn(requestHeaders);
String request = "Hello World";
given(converter.canWrite(String.class, null)).willReturn(true);
converter.write(request, null, this.request);
given(this.request.execute()).willReturn(response);
given(errorHandler.hasError(response)).willReturn(false);
Integer expected = 42;
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.setContentType(textPlain);
responseHeaders.setContentLength(10);
given(response.getStatusCode()).willReturn(HttpStatus.OK);
given(response.getHeaders()).willReturn(responseHeaders);
given(response.getBody()).willReturn(new ByteArrayInputStream(expected.toString().getBytes()));
given(converter.canRead(Integer.class, textPlain)).willReturn(true);
given(converter.read(eq(Integer.class), any(HttpInputMessage.class))).willReturn(expected);
HttpStatus status = HttpStatus.OK;
given(response.getStatusCode()).willReturn(status);
given(response.getStatusText()).willReturn(status.getReasonPhrase());
Integer result = template.patchForObject("http://example.com", request, Integer.class);
assertEquals("Invalid POST result", expected, result);
assertEquals("Invalid Accept header", textPlain.toString(), requestHeaders.getFirst("Accept"));
verify(response).close();
}
@Test
public void patchForObjectNull() throws Exception {
MediaType textPlain = new MediaType("text", "plain");
given(converter.canRead(Integer.class, null)).willReturn(true);
given(converter.getSupportedMediaTypes()).willReturn(Collections.singletonList(textPlain));
given(requestFactory.createRequest(new URI("http://example.com"), HttpMethod.PATCH)).willReturn(request);
HttpHeaders requestHeaders = new HttpHeaders();
given(request.getHeaders()).willReturn(requestHeaders);
given(request.execute()).willReturn(response);
given(errorHandler.hasError(response)).willReturn(false);
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.setContentType(textPlain);
responseHeaders.setContentLength(10);
given(response.getStatusCode()).willReturn(HttpStatus.OK);
given(response.getHeaders()).willReturn(responseHeaders);
given(converter.canRead(Integer.class, textPlain)).willReturn(true);
given(converter.read(Integer.class, response)).willReturn(null);
HttpStatus status = HttpStatus.OK;
given(response.getStatusCode()).willReturn(status);
given(response.getStatusText()).willReturn(status.getReasonPhrase());
Integer result = template.patchForObject("http://example.com", null, Integer.class);
assertNull("Invalid POST result", result);
assertEquals("Invalid content length", 0, requestHeaders.getContentLength());
verify(response).close();
}
@Test
public void delete() throws Exception {
given(requestFactory.createRequest(new URI("http://example.com"), HttpMethod.DELETE)).willReturn(request);