Chúng tôi đã đối mặt với cùng một vấn đề với hành vi hơi khác nhau. Chúng tôi đang sử dụng thư viện cxf apache để thực hiện các cuộc gọi còn lại. Đối với chúng tôi, PATCH đã hoạt động tốt cho đến khi chúng tôi nói chuyện với các dịch vụ giả mạo đang hoạt động trên http. Thời điểm chúng tôi tích hợp với các hệ thống thực tế (hơn https), chúng tôi bắt đầu gặp phải vấn đề tương tự với theo dõi ngăn xếp sau.
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]
Sự cố đã xảy ra trong dòng mã này
connection.setRequestMethod(httpRequestMethod); in URLConnectionHTTPConduit class of cxf library
Bây giờ lý do thực sự cho sự thất bại là
java.net.HttpURLConnection contains a methods variable which looks like below
private static final String[] methods = {
"GET", "POST", "HEAD", "OPTIONS", "PUT", "DELETE", "TRACE"
};
Và chúng ta có thể thấy rằng không có phương thức PATCH nào được định nghĩa do đó lỗi có ý nghĩa. Chúng tôi đã thử rất nhiều thứ khác nhau và xem xét tình trạng tràn ngăn xếp. Câu trả lời hợp lý duy nhất là sử dụng phản chiếu để sửa đổi biến phương thức để đưa vào một giá trị khác "PATCH". Nhưng bằng cách nào đó, chúng tôi không được thuyết phục để sử dụng nó vì giải pháp này là một loại hack và quá nhiều công việc và có thể có tác động vì chúng tôi có thư viện chung để tạo tất cả kết nối và thực hiện các cuộc gọi REST này.
Nhưng sau đó chúng tôi nhận ra rằng bản thân thư viện cxf đang xử lý ngoại lệ và có mã được viết trong khối catch để thêm phương thức còn thiếu bằng cách sử dụng phản chiếu.
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;
}
}
Bây giờ điều này đã cho chúng tôi một số hy vọng, vì vậy chúng tôi đã dành một chút thời gian để đọc mã và nhận thấy rằng nếu chúng tôi cung cấp một thuộc tính cho URLConnectionHTTPConduit.HTTPURL_CONNECTION_METHOD_REFLECTION thì chúng tôi có thể tạo cxf để thực thi trình xử lý ngoại lệ và công việc của chúng tôi được thực hiện theo mặc định, biến sẽ là được gán cho sai do mã bên dưới
DEFAULT_USE_REFLECTION =
Boolean.valueOf(SystemPropertyAction.getProperty(HTTPURL_CONNECTION_METHOD_REFLECTION, "false"));
Vì vậy, đây là những gì chúng tôi phải làm để làm cho công việc này
WebClient.getConfig(client).getRequestContext().put("use.httpurlconnection.method.reflection", true);
hoặc là
WebClient.getConfig(client).getRequestContext().put(HTTPURL_CONNECTION_METHOD_REFLECTION, true);
Nơi WebClient là từ chính thư viện cxf.
Hy vọng câu trả lời này sẽ giúp một số người.