Мы столкнулись с той же проблемой, но с немного другим поведением. Для остальных вызовов мы использовали библиотеку apache cxf. Для нас PATCH работал нормально, пока мы не разговаривали с нашими поддельными сервисами, которые работали через http. В тот момент, когда мы интегрировались с реальными системами (которые были через https), мы столкнулись с той же проблемой со следующей трассировкой стека.
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]
В этой строке кода возникла проблема
connection.setRequestMethod(httpRequestMethod); in URLConnectionHTTPConduit class of cxf library
Настоящая причина неудачи в том, что
java.net.HttpURLConnection contains a methods variable which looks like below
private static final String[] methods = {
"GET", "POST", "HEAD", "OPTIONS", "PUT", "DELETE", "TRACE"
};
И мы видим, что метод PATCH не определен, поэтому ошибка имеет смысл. Мы пробовали много разных вещей и искали переполнение стека. Единственным разумным ответом было использование отражения для изменения переменной метода для вставки другого значения «PATCH». Но почему-то нас не убедили использовать это, поскольку решение было своего рода взломом и требует слишком много работы и может иметь влияние, поскольку у нас была общая библиотека для всех подключений и выполнения этих вызовов REST.
Но потом мы поняли, что сама библиотека cxf обрабатывает исключение, и в блоке catch написан код для добавления недостающего метода с помощью отражения.
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;
}
}
Теперь это вселило в нас надежду, поэтому мы потратили некоторое время на чтение кода и обнаружили, что если мы предоставим свойство для URLConnectionHTTPConduit.HTTPURL_CONNECTION_METHOD_REFLECTION, тогда мы можем заставить cxf выполнить обработчик исключения, и наша работа будет выполнена, поскольку по умолчанию переменная будет иметь вид присвоено false из-за кода ниже
DEFAULT_USE_REFLECTION =
Boolean.valueOf(SystemPropertyAction.getProperty(HTTPURL_CONNECTION_METHOD_REFLECTION, "false"));
Итак, вот что нам нужно было сделать, чтобы это сработало.
WebClient.getConfig(client).getRequestContext().put("use.httpurlconnection.method.reflection", true);
или же
WebClient.getConfig(client).getRequestContext().put(HTTPURL_CONNECTION_METHOD_REFLECTION, true);
Где WebClient - из самой библиотеки cxf.
Надеюсь, этот ответ кому-то поможет.