HttpURLConnection Invalid HTTP method: PATCH
当我尝试通过URLConnection使用非标准的HTTP方法(例如PATCH)时:
1 2 | HttpURLConnection conn = (HttpURLConnection) new URL("http://example.com").openConnection(); conn.setRequestMethod("PATCH"); |
我得到一个例外:
1 2 | java.net.ProtocolException: Invalid HTTP method: PATCH at java.net.HttpURLConnection.setRequestMethod(HttpURLConnection.java:440) |
使用更高级别的API(例如Jersey)会产生相同的错误。是否存在发布PATCH HTTP请求的解决方法?
是的,有解决方法。使用
X-HTTP-Method-Override
。可以在POST请求中使用此标头来伪造?其他HTTP方法。只需将X-HTTP-Method-Override标头的值设置为您要实际执行的HTTP方法。
因此,请使用以下代码。
1 2 | conn.setRequestProperty("X-HTTP-Method-Override","PATCH"); conn.setRequestMethod("POST"); |
有很多好的答案,所以这是我的(不适用于jdk12):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | 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); } } } |
它还使用反射,但是我们不是在侵入每个连接对象,而是在内部检查中使用的是HttpURLConnection#methods静态字段。
OpenJDK中没有解决此问题的错误:https://bugs.openjdk.java.net/browse/JDK-7016595
但是,对于Apache Http-Components Client 4.2,这是可能的。它具有自定义的网络实现,因此可以使用非标准的HTTP方法(例如PATCH)。它甚至有一个支持patch方法的HttpPatch类。
1 2 3 | CloseableHttpClient httpClient = HttpClients.createDefault(); HttpPatch httpPatch = new HttpPatch(new URI("http://example.com")); CloseableHttpResponse response = httpClient.execute(httpPatch); |
行家坐标:
1 2 3 4 5 | <dependency> <groupId>org.apache.httpcomponents</groupId> httpclient</artifactId> <version>4.2+</version> </dependency> |
如果项目在Spring / Gradle上;以下解决方案将进行锻炼。
对于build.gradle,添加以下依赖项;
1 | compile('org.apache.httpcomponents:httpclient:4.5.2') |
并在@SpringBootApplication类中的以下bean中定义以下bean
com.company.project;
1 2 3 4 5 6 7 | @Bean public RestTemplate restTemplate() { HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(); requestFactory.setReadTimeout(600000); requestFactory.setConnectTimeout(600000); return new RestTemplate(requestFactory); } |
此解决方案对我有用。
如果您在Oracle的JRE上使用
反射将不起作用,因为
一个完整的可行解决方案是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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); } } |
我有同样的异常,并以普通的方式编写了套接字解决方案,但我以答案形式将其翻译成Java:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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; } |
我认为它可以在Java中工作。您必须更改服务器和端口号,记住也要更改Host标头,也许您必须捕获一些异常。
致谢
使用答案:
HttpURLConnection Invalid HTTP method: PATCH
我创建了一个示例请求,并且像超级按钮一样工作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | 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; } |
希望对您有帮助。
适用于使用Spring restTemplate寻找详细答案的任何人。
如果将SimpleClientHttpRequestFactory用作restTemplate的ClientHttpRequestFactory,则会遇到问题。
来自java.net.HttpURLConnection:
1 2 3 4 | /* valid HTTP methods */ private static final String[] methods = { "GET","POST","HEAD","OPTIONS","PUT","DELETE","TRACE" }; |
由于不支持PATCH操作,因此将执行同一类的以下代码行:
1 |
我最终使用了@hirosht在他的回答中建议的内容。
另一种肮脏的骇客解决方案是反思:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | 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); } |
在Java 11中,您可以使用HttpRequest类来执行所需的操作:
1 2 3 4 5 6 7 | 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(); |
即使您没有直接访问
)
在API 16的仿真器中,我收到一个异常:
虽然可接受的答案有效,但我想添加一个细节。在新的API中,
1 2 3 4 5 6 | 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); } |
在API 16、19、21中进行测试后,我将
我和泽西岛的客户见识了。
解决方法是:
1 2 | Client client = ClientBuilder.newClient(); client.property(HttpUrlConnectorProvider.SET_METHOD_WORKAROUND, true); |
我们面对相同的问题,但行为略有不同。我们正在使用apache cxf库进行其余的调用。
对于我们来说,PATCH工作得很好,直到我们与通过HTTP工作的虚假服务进行交谈为止。
与实际系统集成(通过https进行集成)的那一刻,我们开始面临以下堆栈跟踪问题。
1 | 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] |
此行代码中发生了问题
1 | connection.setRequestMethod(httpRequestMethod); in URLConnectionHTTPConduit class of cxf library |
现在失败的真正原因是
1 2 3 4 5 | 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" }; |
我们可以看到没有定义PATCH方法,因此该错误是合理的。
我们尝试了许多不同的事情,并查看了堆栈溢出。唯一合理的答案是使用反射修改方法变量以注入另一个值" PATCH"。但是由于某种原因,我们不能说服它使用该解决方案,因为该解决方案是一种hack,而且工作量太大,并且可能会产生影响,因为我们拥有使用公共库进行所有连接并执行这些REST调用的方法。
但是随后我们意识到cxf库本身正在处理异常,并且在catch块中编写了代码,以使用反射添加缺少的方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | 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; } } |
现在这给了我们一些希望,因此我们花了一些时间阅读代码,发现如果我们提供URLConnectionHTTPConduit.HTTPURL_CONNECTION_METHOD_REFLECTION的属性,那么我们可以使cxf执行异常处理程序,并且默认情况下,我们的工作已完成由于以下代码
,变量将被分配为false
1 2 | DEFAULT_USE_REFLECTION = Boolean.valueOf(SystemPropertyAction.getProperty(HTTPURL_CONNECTION_METHOD_REFLECTION,"false")); |
所以这是我们要做的工作
1 | WebClient.getConfig(client).getRequestContext().put("use.httpurlconnection.method.reflection", true); |
或
1 | WebClient.getConfig(client).getRequestContext().put(HTTPURL_CONNECTION_METHOD_REFLECTION, true); |
WebClient来自cxf库本身。
希望此答案对您有所帮助。
1
2
3
4
5
6 **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插件
1 2 3 4 5 6 7 8 9 10 11 12 | > <dependency> > <groupId>org.apache.httpcomponents</groupId> > httpclient</artifactId> > <version>4.3.4</version> > <!-- Exclude Commons Logging in favor of SLF4j --> > <exclusions> > <exclusion> > <groupId>commons-logging</groupId> > commons-logging</artifactId> > </exclusion> > </exclusions> > </dependency> |
使用此方法确实会对您有所帮助
如果您的服务器使用的是ASP.NET Core,则可以简单地添加以下代码,以使用标题
1 2 3 4 5 6 7 |
在调用