Wenn ich versuche, eine nicht standardmäßige HTTP-Methode wie PATCH mit URLConnection zu verwenden:
HttpURLConnection conn = (HttpURLConnection) new URL("http://example.com").openConnection();
conn.setRequestMethod("PATCH");
Ich bekomme eine Ausnahme:
java.net.ProtocolException: Invalid HTTP method: PATCH
at java.net.HttpURLConnection.setRequestMethod(HttpURLConnection.java:440)
Die Verwendung einer übergeordneten API wie Jersey generiert denselben Fehler. Gibt es eine Problemumgehung, um eine PATCH-HTTP-Anforderung auszugeben?
java
httpurlconnection
kavai77
quelle
quelle
POST
und dasX-HTTP-Method-Override
Feld verstehen . Siehe stackoverflow.com/a/46323891/3647724 für eine bessere und aktuelle LösungEs gibt viele gute Antworten, also hier ist meine (funktioniert nicht 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); } } }
Es wird auch Reflektion verwendet, aber anstatt in jedes Verbindungsobjekt zu hacken, hacken wir das statische Feld der HttpURLConnection # -Methoden, das bei den internen Überprüfungen verwendet wird.
quelle
In OpenJDK gibt es einen Fehler, der nicht behoben werden kann: https://bugs.openjdk.java.net/browse/JDK-7016595
Mit Apache Http-Components Client 4.2+ ist dies jedoch möglich. Es verfügt über eine benutzerdefinierte Netzwerkimplementierung, sodass die Verwendung von nicht standardmäßigen HTTP-Methoden wie PATCH möglich ist. Es gibt sogar eine HttpPatch-Klasse, die die Patch-Methode unterstützt.
CloseableHttpClient httpClient = HttpClients.createDefault(); HttpPatch httpPatch = new HttpPatch(new URI("http://example.com")); CloseableHttpResponse response = httpClient.execute(httpPatch);
Maven-Koordinaten:
<dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.2+</version> </dependency>
quelle
Wenn das Projekt auf Spring / Gradle läuft ; Die folgende Lösung wird trainieren.
Fügen Sie für das build.gradle die folgende Abhängigkeit hinzu:
compile('org.apache.httpcomponents:httpclient:4.5.2')
Definieren Sie die folgende Bean in Ihrer @ SpringBootApplication-Klasse im com.company.project.
@Bean public RestTemplate restTemplate() { HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(); requestFactory.setReadTimeout(600000); requestFactory.setConnectTimeout(600000); return new RestTemplate(requestFactory); }
Diese Lösung hat bei mir funktioniert.
quelle
Ich hatte die gleiche Ausnahme und schrieb Sockets-Lösung (in groovig), aber ich übersetze im Antwortformular Java für Sie:
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; }
Ich denke, es funktioniert in Java. Sie müssen den Server und die Portnummer ändern. Denken Sie daran, auch den Host-Header zu ändern. Möglicherweise müssen Sie eine Ausnahme abfangen.
Freundliche Grüße
quelle
\r\n
, nicht als was auch immer vorgesehenprintln()
ist.Reflexion , wie in diesem Beitrag beschrieben und ein damit verbundener Beitrag funktioniert nicht, wenn Sie ein verwenden
HttpsURLConnection
auf Oracle JRE, dasun.net.www.protocol.https.HttpsURLConnectionImpl
ist die Verwendung vonmethod
Feld von derjava.net.HttpURLConnection
seineDelegateHttpsURLConnection
!Eine vollständig funktionierende Lösung ist also:
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); } }
quelle
Mit der Antwort:
Ich habe eine Beispielanfrage erstellt und arbeite wie ein Zauber:
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; }
Ich hoffe es hilft dir.
quelle
Für alle, die Spring restTemplate verwenden und nach einer detaillierten Antwort suchen.
Sie werden mit dem Problem konfrontiert, wenn Sie SimpleClientHttpRequestFactory als ClientHttpRequestFactory Ihres restTemplate verwenden.
Von java.net.HttpURLConnection:
/* valid HTTP methods */ private static final String[] methods = { "GET", "POST", "HEAD", "OPTIONS", "PUT", "DELETE", "TRACE" };
Da PATCH keine unterstützte Operation ist, wird diese Codezeile derselben Klasse ausgeführt:
throw new ProtocolException("Invalid HTTP method: " + method);
Am Ende habe ich dasselbe verwendet, was @hirosht in seiner Antwort vorgeschlagen hat .
quelle
Eine andere schmutzige Hack-Lösung ist 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); }
quelle
Sie können eine detaillierte Lösung finden, die auch dann funktioniert, wenn Sie keinen direkten Zugriff auf die haben
HttpUrlConnection
(wie bei der Arbeit mit Jersey Client hier: PATCH-Anfrage mit Jersey Clientquelle
Wenn Ihr Server ASP.NET Core verwendet, können Sie einfach den folgenden Code hinzufügen, um die HTTP-Methode mithilfe des Headers anzugeben
X-HTTP-Method-Override
, wie in der akzeptierten Antwort beschrieben .app.Use((context, next) => { var headers = context.Request.Headers["X-HTTP-Method-Override"]; if(headers.Count == 1) { context.Request.Method = headers.First(); } return next(); });
Fügen Sie diesen Code einfach
Startup.Configure
vor Ihrem Anruf bei hinzuapp.UseMvc()
.quelle
Im Emulator von API 16 habe ich eine Ausnahme erhalten:
java.net.ProtocolException: Unknown method 'PATCH'; must be one of [OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE]
.Während eine akzeptierte Antwort funktioniert, möchte ich ein Detail hinzufügen. In neuen APIs
PATCH
funktioniert dies gut. In Verbindung mit https://github.com/OneDrive/onedrive-sdk-android/issues/16 sollten Sie Folgendes schreiben: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); }
Ich
JELLY_BEAN_MR2
habeKITKAT
nach dem Testen in API 16, 19, 21 zu gewechselt .quelle
Ich habe meine mit Jersey-Kunden. Die Problemumgehung war:
Client client = ClientBuilder.newClient(); client.property(HttpUrlConnectorProvider.SET_METHOD_WORKAROUND, true);
quelle
Wir haben das gleiche Problem mit leicht unterschiedlichem Verhalten konfrontiert. Wir haben die Apache-CXF-Bibliothek verwendet, um die restlichen Anrufe zu tätigen. Für uns funktionierte PATCH einwandfrei, bis wir mit unseren gefälschten Diensten sprachen, die über http funktionierten. In dem Moment, in dem wir uns in tatsächliche Systeme integriert haben (die über https lagen), hatten wir das gleiche Problem mit der folgenden Stapelverfolgung.
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]
In dieser Codezeile ist ein Problem aufgetreten
connection.setRequestMethod(httpRequestMethod); in URLConnectionHTTPConduit class of cxf library
Der wahre Grund für das Scheitern ist das
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" };
Und wir können sehen, dass keine PATCH-Methode definiert ist, daher machte der Fehler Sinn. Wir haben viele verschiedene Dinge ausprobiert und uns den Stapelüberlauf angesehen. Die einzig vernünftige Antwort bestand darin, die Methodenvariable mithilfe der Reflexion zu ändern, um einen weiteren Wert "PATCH" einzufügen. Aber irgendwie waren wir nicht davon überzeugt, dies zu verwenden, da die Lösung eine Art Hack war und zu viel Arbeit ist und Auswirkungen haben könnte, da wir eine gemeinsame Bibliothek hatten, um alle Verbindungen herzustellen und diese REST-Aufrufe auszuführen.
Aber dann haben wir festgestellt, dass die cxf-Bibliothek selbst die Ausnahme behandelt und im catch-Block Code geschrieben ist, um die fehlende Methode mithilfe von Reflection hinzuzufügen.
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; } }
Dies gab uns einige Hoffnungen, so dass wir einige Zeit mit dem Lesen des Codes verbracht haben und festgestellt haben, dass wir, wenn wir eine Eigenschaft für URLConnectionHTTPConduit.HTTPURL_CONNECTION_METHOD_REFLECTION bereitstellen, cxf zum Ausführen des Ausnahmebehandlungsprogramms verwenden können und unsere Arbeit standardmäßig ausgeführt wird aufgrund des folgenden Codes false zugewiesen
DEFAULT_USE_REFLECTION = Boolean.valueOf(SystemPropertyAction.getProperty(HTTPURL_CONNECTION_METHOD_REFLECTION, "false"));
Hier ist also, was wir tun mussten, damit dies funktioniert
WebClient.getConfig(client).getRequestContext().put("use.httpurlconnection.method.reflection", true);
oder
WebClient.getConfig(client).getRequestContext().put(HTTPURL_CONNECTION_METHOD_REFLECTION, true);
Wo WebClient aus der cxf-Bibliothek selbst stammt.
Hoffe diese Antwort hilft jemandem.
quelle
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>
Verwenden Sie dies wirklich, es würde Ihnen helfen
quelle
In Java 11+ können Sie die HttpRequest-Klasse verwenden, um das zu tun, was Sie wollen:
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();
quelle