Doc: Can't issue PATCH request using RestTemplate with SimpleClientHttpRequestFactory [SPR-15052]

See original GitHub issue

Behrang 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:

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:closed
  • Created 7 years ago
  • Reactions:1
  • Comments:7

github_iconTop GitHub Comments

3reactions
spring-projects-issuescommented, Jan 11, 2019

Kazuki Shimizu commented

Is this specific to TestRestTemplate, as your title suggests?

No. I tried to send PATCH request using RestTemplate with SimpleClientHttpRequestFactory. As result, it is same result as follow: I think this behavior is known limitation.

...
Caused by: java.net.ProtocolException: Invalid HTTP method: PATCH
	at java.net.HttpURLConnection.setRequestMethod(HttpURLConnection.java:440)
	at sun.net.www.protocol.http.HttpURLConnection.setRequestMethod(HttpURLConnection.java:519)
	at org.springframework.http.client.SimpleClientHttpRequestFactory.prepareConnection(SimpleClientHttpRequestFactory.java:216)
...

If you want to use the PATCH method, you can use the RestTemplate with some OSS libraries as follow:

  • Apache HttpComponents HttpClient
  • OkHttp 3
  • OkHttp 2
  • Netty 4

I tried to send a PATCH request using HttpComponentsClientHttpRequestFactory, Netty4ClientHttpRequestFactory, OkHttp3ClientHttpRequestFactory and OkHttpClientHttpRequestFactory. These work fine.

Thanks.

2reactions
spring-projects-issuescommented, Jan 11, 2019

Kazuki Shimizu commented

As one alternative… If you use the HiddenHttpMethodFilter on your application(or target web application), you can access using POST method with _method=patch parameter as follow:

RestTemplate restTemplate = new RestTemplate();
restTemplate.postForObject("http://localhost:8080/persons/1?_method=patch", requestBody, String.class);
Read more comments on GitHub >

github_iconTop Results From Across the Web

json - RestTemplate PATCH request - Stack Overflow
I solved this problem just adding a new HttpRequestFactory to my restTemplate instance. Like this RestTemplate restTemplate = new ...
Read more >
Spring Boot - RestTemplate PATCH request fix - Rake's Artifacts
Http PATCH using RestTemplate in Spring Boot. ... In Spring Boot, you make a simple http request as below: 1. Define RestTemplate bean....
Read more >
RestTemplate (Spring Framework 6.0.2 API)
Synchronous client to perform HTTP requests, exposing a simple, template method API ... Create a new instance of the RestTemplate using default settings....
Read more >
Complete Guide to Spring RestTemplate - Reflectoring
In this tutorial, we will understand how to use RestTemplate for invoking REST APIs of different shapes. Example Code. This article is ...
Read more >
Rest Template : I/O error on PATCH request for “http ...
Rest Template : I/O error on PATCH request for “http://localhost:8080/api/xxx".. พระเอกของเราคือ HttpComponentsClientHttpRequestFactory นั่นเองครับ.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found