Mieliśmy ten sam problem z nieco innym zachowaniem. Do wykonywania pozostałych wywołań używaliśmy biblioteki Apache cxf. Dla nas PATCH działał dobrze, dopóki nie rozmawialiśmy z naszymi fałszywymi usługami, które działały przez http. W chwili, gdy integrujemy się z rzeczywistymi systemami (które były przez https), zaczęliśmy napotykać ten sam problem z śledzeniem stosu.
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]
Problem występował w tej linii kodu
connection.setRequestMethod(httpRequestMethod); in URLConnectionHTTPConduit class of cxf library
Teraz prawdziwym powodem niepowodzenia jest to
java.net.HttpURLConnection contains a methods variable which looks like below
private static final String[] methods = {
"GET", "POST", "HEAD", "OPTIONS", "PUT", "DELETE", "TRACE"
};
I widzimy, że nie ma zdefiniowanej metody PATCH, stąd błąd miał sens. Próbowaliśmy wielu różnych rzeczy i przyjrzeliśmy się przepełnieniu stosu. Jedyną rozsądną odpowiedzią było użycie refleksji w celu zmodyfikowania zmiennej Method, aby wprowadzić inną wartość „PATCH”. Ale jakoś nie byliśmy przekonani, aby to wykorzystać, ponieważ rozwiązanie było trochę hackem i jest zbyt pracochłonne i może mieć wpływ, ponieważ mieliśmy wspólną bibliotekę do nawiązywania wszystkich połączeń i wykonywania tych wywołań REST.
Ale potem zdaliśmy sobie sprawę, że sama biblioteka cxf obsługuje wyjątek i jest kod napisany w bloku catch, aby dodać brakującą metodę za pomocą odbicia.
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) {
logStackTrace(t);
}
}
ReflectionUtil.setAccessible(f).set(connection, httpRequestMethod);
message.put(HTTPURL_CONNECTION_METHOD_REFLECTION, true);
} catch (Throwable t) {
logStackTrace(t);
throw ex;
}
}
Teraz dało nam to pewne nadzieje, więc spędziliśmy trochę czasu na czytaniu kodu i stwierdziliśmy, że jeśli dostarczymy właściwość dla URLConnectionHTTPConduit.HTTPURL_CONNECTION_METHOD_REFLECTION, wtedy możemy sprawić, że cxf uruchomi procedurę obsługi wyjątków i nasza praca będzie wykonywana domyślnie zmienna będzie przypisano wartość false z powodu poniższego kodu
DEFAULT_USE_REFLECTION =
Boolean.valueOf(SystemPropertyAction.getProperty(HTTPURL_CONNECTION_METHOD_REFLECTION, "false"));
Oto, co musieliśmy zrobić, aby to zadziałało
WebClient.getConfig(client).getRequestContext().put("use.httpurlconnection.method.reflection", true);
lub
WebClient.getConfig(client).getRequestContext().put(HTTPURL_CONNECTION_METHOD_REFLECTION, true);
Gdzie WebClient pochodzi z samej biblioteki cxf.
Mam nadzieję, że ta odpowiedź komuś pomoże.