Doc: Can't issue PATCH request using RestTemplate with SimpleClientHttpRequestFactory [SPR-15052]
See original GitHub issueBehrang Saeedzadeh opened SPR-15052 and commented
This test case fails with at least OpenJDK 1.8.0_101 under Ubuntu (4.8.0-30-generic):
@Test
@DirtiesContext
public void testPatchMethod() {
final HttpEntity<Object> request = new HttpEntity<>("<echo>Hello</echo>");
restTemplate.exchange("/patchy/echo", PATCH, request, String.class);
}
with the following exception:
org.springframework.web.client.ResourceAccessException: I/O error on PATCH request for "http://localhost:44661/persons/1": Invalid HTTP method: PATCH; nested exception is java.net.ProtocolException: Invalid HTTP method: PATCH
This is due to HttpURLConnection
only allowing the following HTTP methods:
/* valid HTTP methods */
private static final String[] methods = {
"GET", "POST", "HEAD", "OPTIONS", "PUT", "DELETE", "TRACE"
};
PayPal guys have implemented a workaround for their own API here:
/**
* Workaround for a bug in {@code HttpURLConnection.setRequestMethod(String)}
* The implementation of Sun/Oracle is throwing a {@code ProtocolException}
* when the method is other than the HTTP/1.1 default methods. So to use {@code PATCH}
* and others, we must apply this workaround.
*
* See issue http://java.net/jira/browse/JERSEY-639
*/
private static void setRequestMethodViaJreBugWorkaround(final HttpURLConnection httpURLConnection, final String method) {
try {
httpURLConnection.setRequestMethod(method); // Check whether we are running on a buggy JRE
} catch (final ProtocolException pe) {
try {
final Class<?> httpURLConnectionClass = httpURLConnection.getClass();
AccessController
.doPrivileged(new PrivilegedExceptionAction<Object>() {
public Object run() throws NoSuchFieldException,
IllegalAccessException {
try {
httpURLConnection.setRequestMethod(method);
// Check whether we are running on a buggy
// JRE
} catch (final ProtocolException pe) {
Class<?> connectionClass = httpURLConnection
.getClass();
Field delegateField = null;
try {
delegateField = connectionClass
.getDeclaredField("delegate");
delegateField.setAccessible(true);
HttpURLConnection delegateConnection = (HttpURLConnection) delegateField
.get(httpURLConnection);
setRequestMethodViaJreBugWorkaround(
delegateConnection, method);
} catch (NoSuchFieldException e) {
// Ignore for now, keep going
} catch (IllegalArgumentException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
try {
Field methodField;
while (connectionClass != null) {
try {
methodField = connectionClass
.getDeclaredField("method");
} catch (NoSuchFieldException e) {
connectionClass = connectionClass
.getSuperclass();
continue;
}
methodField.setAccessible(true);
methodField.set(httpURLConnection,
method);
break;
}
} catch (final Exception e) {
throw new RuntimeException(e);
}
}
return null;
}
});
} catch (final PrivilegedActionException e) {
final Throwable cause = e.getCause();
if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
} else {
throw new RuntimeException(cause);
}
}
}
}
Another alternative is to use Apache Http-Components Client 4.2+ instead.
I have attached a test case reproducing the issue (see org.behrang.howto.bugreport.PatchyControllerTest#testPatchMethod
).
Affects: 4.3.5
Attachments:
- bug-report.zip (57.62 kB)
Issue Links:
- #19910 Enhance RestTemplate to support PATCH out of the box
Referenced from: commits https://github.com/spring-projects/spring-framework/commit/048098119efedbea0461539623188ed2af785bb7, https://github.com/spring-projects/spring-framework/commit/20aaa8841c56ed0ddd55e6f7e8f807acf6699f57
0 votes, 5 watchers
Issue Analytics
- State:
- Created 7 years ago
- Reactions:1
- Comments:7
Kazuki Shimizu commented
No. I tried to send PATCH request using
RestTemplate
withSimpleClientHttpRequestFactory
. As result, it is same result as follow: I think this behavior is known limitation.If you want to use the
PATCH
method, you can use theRestTemplate
with some OSS libraries as follow:I tried to send a
PATCH
request usingHttpComponentsClientHttpRequestFactory
,Netty4ClientHttpRequestFactory
,OkHttp3ClientHttpRequestFactory
andOkHttpClientHttpRequestFactory
. These work fine.Thanks.
Kazuki Shimizu commented
As one alternative… If you use the
HiddenHttpMethodFilter
on your application(or target web application), you can access usingPOST
method with_method=patch
parameter as follow: