HttpURLConnection Invalid HTTP method: PATCH

JavaHttpurlconnection

Java Problem Overview


When I try to use a non-standard HTTP Method like PATCH with URLConnection:

    HttpURLConnection conn = (HttpURLConnection) new URL("http://example.com").openConnection();
    conn.setRequestMethod("PATCH");

I get an exception:

java.net.ProtocolException: Invalid HTTP method: PATCH
at java.net.HttpURLConnection.setRequestMethod(HttpURLConnection.java:440)

Using a higher level API like Jersey generates the same error. Is there a workaround to issue a PATCH HTTP request?

Java Solutions


Solution 1 - Java

There are a lot of good answers, so here is mine (not work in jdk12):

import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.Set;

public class SupportPatch {
    public static void main(String... args) throws IOException {
        allowMethods("PATCH");

        HttpURLConnection conn = (HttpURLConnection) new URL("http://example.com").openConnection();
        conn.setRequestMethod("PATCH");
    }

    private static void allowMethods(String... methods) {
        try {
            Field methodsField = HttpURLConnection.class.getDeclaredField("methods");

            Field modifiersField = Field.class.getDeclaredField("modifiers");
            modifiersField.setAccessible(true);
            modifiersField.setInt(methodsField, methodsField.getModifiers() & ~Modifier.FINAL);

            methodsField.setAccessible(true);

            String[] oldMethods = (String[]) methodsField.get(null);
            Set<String> methodsSet = new LinkedHashSet<>(Arrays.asList(oldMethods));
            methodsSet.addAll(Arrays.asList(methods));
            String[] newMethods = methodsSet.toArray(new String[0]);

            methodsField.set(null/*static field*/, newMethods);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            throw new IllegalStateException(e);
        }
    }
}

It also uses reflection, but instead of hacking into every connection object we're hacking HttpURLConnection#methods static field which is used in the checks internally.

Solution 2 - Java

Yes there is workaround for this. Use

> X-HTTP-Method-Override

. This header can be used in a POST request to “fake” other HTTP methods. Simply set the value of the X-HTTP-Method-Override header to the HTTP method you would like to actually perform. So use following code.

conn.setRequestProperty("X-HTTP-Method-Override", "PATCH");
conn.setRequestMethod("POST");

Solution 3 - Java

There is a Won't Fix bug in OpenJDK for this: https://bugs.openjdk.java.net/browse/JDK-7016595

However, with Apache Http-Components Client 4.2+ this is possible. It has a custom networking implementation, thus using all standard HTTP methods like PATCH is possible. It even has a HttpPatch class supporting the patch method.

CloseableHttpClient httpClient = HttpClients.createDefault();
HttpPatch httpPatch = new HttpPatch(new URI("http://example.com"));
CloseableHttpResponse response = httpClient.execute(httpPatch);

Maven Coordinates:

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.2+</version>
</dependency>

Solution 4 - Java

If the project is on Spring/Gradle; the following solution will workout.

For the build.gradle, add the following dependency;

compile('org.apache.httpcomponents:httpclient:4.5.2')

And define the following bean in your @SpringBootApplication class inside the com.company.project;

 @Bean
 public RestTemplate restTemplate() {
  HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
  requestFactory.setReadTimeout(600000);
  requestFactory.setConnectTimeout(600000);
  return new RestTemplate(requestFactory);
 }

This solutions worked for me.

Solution 5 - Java

Reflection as described in this post and a related post does not work if you are using a HttpsURLConnection on Oracle's JRE, becausesun.net.www.protocol.https.HttpsURLConnectionImpl is using the method field from the java.net.HttpURLConnection of its DelegateHttpsURLConnection!

So a complete working solution is:

private void setRequestMethod(final HttpURLConnection c, final String value) {
    try {
        final Object target;
        if (c instanceof HttpsURLConnectionImpl) {
            final Field delegate = HttpsURLConnectionImpl.class.getDeclaredField("delegate");
            delegate.setAccessible(true);
            target = delegate.get(c);
        } else {
            target = c;
        }
        final Field f = HttpURLConnection.class.getDeclaredField("method");
        f.setAccessible(true);
        f.set(target, value);
    } catch (IllegalAccessException | NoSuchFieldException ex) {
        throw new AssertionError(ex);
    }
}

Solution 6 - Java

In java 11+ you can use the HttpRequest class to do what you want:

import java.net.http.HttpRequest;

HttpRequest request = HttpRequest.newBuilder()
               .uri(URI.create(uri))
               .method("PATCH", HttpRequest.BodyPublishers.ofString(message))
               .header("Content-Type", "text/xml")
               .build();

Solution 7 - Java

I had the same exception and wrote sockets solution (in groovy) but i traslate in the answer form to java for you:

String doInvalidHttpMethod(String method, String resource){
        Socket s = new Socket(InetAddress.getByName("google.com"), 80);
        PrintWriter pw = new PrintWriter(s.getOutputStream());
        pw.println(method +" "+resource+" HTTP/1.1");
        pw.println("User-Agent: my own");
        pw.println("Host: google.com:80");
        pw.println("Content-Type: */*");
        pw.println("Accept: */*");
        pw.println("");
        pw.flush();
        BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
        String t = null;
        String response = ""; 
        while((t = br.readLine()) != null){
            response += t;
        }
        br.close();
        return response;
    }

I think it work in java. You have to change the server and port number remember change the Host header too and Maybe you have to catch some exception.

Best Regards

Solution 8 - Java

Using the answer:

> https://stackoverflow.com/questions/25163131/httpurlconnection-invalid-http-method-patch/32503192#32503192

I'm created a sample request and work like a charm:

public void request(String requestURL, String authorization, JsonObject json) {

    try {

        URL url = new URL(requestURL);
        httpConn = (HttpURLConnection) url.openConnection();
        httpConn.setRequestMethod("POST");
        httpConn.setRequestProperty("X-HTTP-Method-Override", "PATCH");
        httpConn.setRequestProperty("Content-Type", "application/json");
        httpConn.setRequestProperty("Authorization", authorization);
        httpConn.setRequestProperty("charset", "utf-8");

        DataOutputStream wr = new DataOutputStream(httpConn.getOutputStream());
        wr.writeBytes(json.toString());
        wr.flush();
        wr.close();

        httpConn.connect();

        String response = finish();

        if (response != null && !response.equals("")) {
            created = true;
        }
    } 
    catch (Exception e) {
        e.printStackTrace();
    }
}

public String finish() throws IOException {

    String response = "";

    int status = httpConn.getResponseCode();
    if (status == HttpURLConnection.HTTP_OK || status == HttpURLConnection.HTTP_CREATED) {
        BufferedReader reader = new BufferedReader(new InputStreamReader(
                httpConn.getInputStream()));
        String line = null;
        while ((line = reader.readLine()) != null) {
            response += line;
        }
        reader.close();
        httpConn.disconnect();
    } else {
        throw new IOException("Server returned non-OK status: " + status);
    }

    return response;
}

I hope it help you.

Solution 9 - Java

For anyone using Spring restTemplate looking for a detailed answer.

You will face the problem if you are using SimpleClientHttpRequestFactory as your restTemplate's ClientHttpRequestFactory.

From java.net.HttpURLConnection:

/* valid HTTP methods */
private static final String[] methods = {
    "GET", "POST", "HEAD", "OPTIONS", "PUT", "DELETE", "TRACE"
};

As PATCH is not a supported operation, this line of code from the same class will execute:

throw new ProtocolException("Invalid HTTP method: " + method);

I ended up using the same as what @hirosht suggested in his answer.

Solution 10 - Java

Another dirty hack solution is reflexion:

private void setVerb(HttpURLConnection cn, String verb) throws IOException {

  switch (verb) {
    case "GET":
    case "POST":
    case "HEAD":
    case "OPTIONS":
    case "PUT":
    case "DELETE":
    case "TRACE":
      cn.setRequestMethod(verb);
      break;
    default:
      // set a dummy POST verb
      cn.setRequestMethod("POST");
      try {
        // Change protected field called "method" of public class HttpURLConnection
        setProtectedFieldValue(HttpURLConnection.class, "method", cn, verb);
      } catch (Exception ex) {
        throw new IOException(ex);
      }
      break;
  }
}

public static <T> void setProtectedFieldValue(Class<T> clazz, String fieldName, T object, Object newValue) throws Exception {
    Field field = clazz.getDeclaredField(fieldName);

    field.setAccessible(true);
    field.set(object, newValue);
 }

Solution 11 - Java

You can find a detailed solution that can work even if you don't have direct access to the HttpUrlConnection (like when working with Jersey Client here: https://stackoverflow.com/questions/22355235/patch-request-using-jersey-client/39641592#39641592

Solution 12 - Java

If your server is using ASP.NET Core, you can simply add the following code to specify the HTTP method using the header X-HTTP-Method-Override, as described in the accepted answer.

app.Use((context, next) => {
	var headers = context.Request.Headers["X-HTTP-Method-Override"];
	if(headers.Count == 1) {
		context.Request.Method = headers.First();
	}
	return next();
});

Simply add this code in Startup.Configure before your call to app.UseMvc().

Solution 13 - Java

In emulator of API 16 I received an exception: java.net.ProtocolException: Unknown method 'PATCH'; must be one of [OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE].

While an accepted answer works, I want to add one detail. In new APIs PATCH works well, so in conjunction with https://github.com/OneDrive/onedrive-sdk-android/issues/16 you should write:

if (method.equals("PATCH") && Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
	httpConnection.setRequestProperty("X-HTTP-Method-Override", "PATCH");
	httpConnection.setRequestMethod("POST");
} else {
	httpConnection.setRequestMethod(method);
}

I changed JELLY_BEAN_MR2 to KITKAT after testing in API 16, 19, 21.

Solution 14 - Java

I got mine with Jersey client. The workaround was:

Client client = ClientBuilder.newClient();
client.property(HttpUrlConnectorProvider.SET_METHOD_WORKAROUND, true);

Solution 15 - Java

We have faced the same problem with slightly different behavior. We were using apache cxf library for making the rest calls. For us, PATCH was working fine till we were talking to our fake services which were working over http. The moment we integrated with actual systems (which were over https) we started facing the same issue with following stack trace.

java.net.ProtocolException: Invalid HTTP method: PATCH	at java.net.HttpURLConnection.setRequestMethod(HttpURLConnection.java:428) ~[na:1.7.0_51]	at sun.net.www.protocol.https.HttpsURLConnectionImpl.setRequestMethod(HttpsURLConnectionImpl.java:374) ~[na:1.7.0_51]	at org.apache.cxf.transport.http.URLConnectionHTTPConduit.setupConnection(URLConnectionHTTPConduit.java:149) ~[cxf-rt-transports-http-3.1.14.jar:3.1.14]

Issue was happening in this line of code

connection.setRequestMethod(httpRequestMethod); in URLConnectionHTTPConduit class of cxf library

Now the real reason for the failure is that

java.net.HttpURLConnection contains a methods variable which looks like below
/* valid HTTP methods */
    private static final String[] methods = {
        "GET", "POST", "HEAD", "OPTIONS", "PUT", "DELETE", "TRACE"
    };

And we can see that there is no PATCH method defined hence the error made sense. We tried lots of different thing and looked over stack overflow. The only reasonable answer was to use reflection to modify the methods variable to inject another value "PATCH". But somehow we were not convinced to use that as the solution was kind of hack and is too much work and might have impact as we had common library to make all connection and performing these REST calls.

But then we realized that cxf library itself is handling the exception and there is code written in the catch block to add the missing method using reflection.

try {
        connection.setRequestMethod(httpRequestMethod);
    } catch (java.net.ProtocolException ex) {
        Object o = message.getContextualProperty(HTTPURL_CONNECTION_METHOD_REFLECTION);
        boolean b = DEFAULT_USE_REFLECTION;
        if (o != null) {
            b = MessageUtils.isTrue(o);
        }
        if (b) {
            try {
                java.lang.reflect.Field f = ReflectionUtil.getDeclaredField(HttpURLConnection.class, "method");
                if (connection instanceof HttpsURLConnection) {
                    try {
                        java.lang.reflect.Field f2 = ReflectionUtil.getDeclaredField(connection.getClass(),
                                                                                     "delegate");
                        Object c = ReflectionUtil.setAccessible(f2).get(connection);
                        if (c instanceof HttpURLConnection) {
                            ReflectionUtil.setAccessible(f).set(c, httpRequestMethod);
                        }

                        f2 = ReflectionUtil.getDeclaredField(c.getClass(), "httpsURLConnection");
                        HttpsURLConnection c2 = (HttpsURLConnection)ReflectionUtil.setAccessible(f2)
                                .get(c);

                        ReflectionUtil.setAccessible(f).set(c2, httpRequestMethod);
                    } catch (Throwable t) {
                        //ignore
                        logStackTrace(t);
                    }
                }
                ReflectionUtil.setAccessible(f).set(connection, httpRequestMethod);
                message.put(HTTPURL_CONNECTION_METHOD_REFLECTION, true);
            } catch (Throwable t) {
                logStackTrace(t);
                throw ex;
            }
        }

Now this gave us some hopes, so we spent some time in reading the code and found that if we provide a property for URLConnectionHTTPConduit.HTTPURL_CONNECTION_METHOD_REFLECTION then we can make cxf to execute the exception handler and our work is done as by default the variable will be assigned to false due to below code

DEFAULT_USE_REFLECTION = 
        Boolean.valueOf(SystemPropertyAction.getProperty(HTTPURL_CONNECTION_METHOD_REFLECTION, "false"));

So here is what we had to do to make this work

WebClient.getConfig(client).getRequestContext().put("use.httpurlconnection.method.reflection", true);

or

WebClient.getConfig(client).getRequestContext().put(HTTPURL_CONNECTION_METHOD_REFLECTION, true);

Where WebClient is from cxf library itself.

Hope this answer helps some one.

Solution 16 - Java

> CloseableHttpClient http = HttpClientBuilder.create().build(); > HttpPatch updateRequest = new HttpPatch("URL"); > updateRequest.setEntity(new StringEntity("inputjsonString", ContentType.APPLICATION_JSON)); > updateRequest.setHeader("Bearer", "auth"); > HttpResponse response = http.execute(updateRequest); > JSONObject result = new JSONObject(IOUtils.toString(response.getEntity().getContent()));

maven plugin


> <dependency>
>                 <groupId>org.apache.httpcomponents</groupId>
>                 <artifactId>httpclient</artifactId>
>                 <version>4.3.4</version>
>                 <!-- Exclude Commons Logging in favor of SLF4j -->
>                 <exclusions>
>                     <exclusion>
>                         <groupId>commons-logging</groupId>
>                         <artifactId>commons-logging</artifactId>
>                     </exclusion>
>                 </exclusions>	
>             </dependency>

use this really it would helps you

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionkavai77View Question on Stackoverflow
Solution 1 - JavaokutaneView Answer on Stackoverflow
Solution 2 - JavasagarView Answer on Stackoverflow
Solution 3 - Javakavai77View Answer on Stackoverflow
Solution 4 - JavahiroshtView Answer on Stackoverflow
Solution 5 - JavarmullerView Answer on Stackoverflow
Solution 6 - Javasimonalexander2005View Answer on Stackoverflow
Solution 7 - JavamoralejaSinCuentoNiProverbioView Answer on Stackoverflow
Solution 8 - JavaDuan BressanView Answer on Stackoverflow
Solution 9 - JavaJohnuView Answer on Stackoverflow
Solution 10 - JavadreamtangerineView Answer on Stackoverflow
Solution 11 - JavaDaniel L.View Answer on Stackoverflow
Solution 12 - JavaPeppe L-GView Answer on Stackoverflow
Solution 13 - JavaCoolMindView Answer on Stackoverflow
Solution 14 - JavaDaniel JipaView Answer on Stackoverflow
Solution 15 - JavaSanjay BharwaniView Answer on Stackoverflow
Solution 16 - Javaperumal ksView Answer on Stackoverflow