HttpURLConnection अमान्य HTTP विधि: PATCH


81

जब मैं URL के साथ PATCH जैसे गैर-मानक HTTP विधि का उपयोग करने का प्रयास करता हूं:

    HttpURLConnection conn = (HttpURLConnection) new URL("http://example.com").openConnection();
    conn.setRequestMethod("PATCH");

मुझे एक अपवाद मिलता है:

java.net.ProtocolException: Invalid HTTP method: PATCH
at java.net.HttpURLConnection.setRequestMethod(HttpURLConnection.java:440)

जर्सी जैसे उच्च स्तर के एपीआई का उपयोग करना एक ही त्रुटि उत्पन्न करता है। वहाँ एक HTTP HTTP अनुरोध जारी करने के लिए एक समाधान है?

जवाबों:


48

हां इसके लिए वर्कअराउंड है। उपयोग

एक्स-HTTP-मेथड-ओवरराइड

। इस शीर्ष लेख का उपयोग POST अनुरोध में अन्य HTTP विधियों "नकली" के लिए किया जा सकता है। बस उस HTTP विधि के लिए X-HTTP-Method-Over -ride हेडर का मान सेट करें जिसे आप वास्तव में प्रदर्शन करना चाहते हैं। इसलिए निम्नलिखित कोड का उपयोग करें।

conn.setRequestProperty("X-HTTP-Method-Override", "PATCH");
conn.setRequestMethod("POST");

34
यह केवल तभी काम करता है जब प्राप्त अंत इसका समर्थन करता है। यह अभी भी लाइन के नीचे "POST" भेजता है।
जोसेफ जैक्विंटा

3
यदि रिसीवर इसका समर्थन करता है, तो (मेरे लिए) यह आगे बढ़ने का सबसे साफ तरीका है।
मैक्सिमे टी

4
फायरबेस रेस्ट एपीआई को कॉल करने के लिए HttpUrlConnection का उपयोग करते समय यह विधि काम करती है।
एंड्रयू केली

1
@ डुआनब्रेसन प्रोटोकॉल तब तक एक मुद्दा नहीं होना चाहिए जब तक कि सर्वर या तो समर्थन करता है या दोनों (यह केवल HTTPS को कनेक्शन स्वीकार करना चाहिए। हालांकि)
एलेक्सिस विल्के

3
यह एक मान्य उत्तर नहीं है क्योंकि यह समस्या का समाधान नहीं करता है। सर्वर को आपको उपयोग करने की अनुमति देनी होगी POSTऔर X-HTTP-Method-Overrideक्षेत्र को समझना होगा । एक वास्तविक सुधार के लिए stackoverflow.com/a/46323891/3647724 देखें
Feirell

51

बहुत सारे अच्छे उत्तर हैं, इसलिए यहाँ मेरा (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);
        }
    }
}

यह प्रतिबिंब का भी उपयोग करता है, लेकिन हर कनेक्शन ऑब्जेक्ट में हैकिंग के बजाय हम HttpURLConnection # विधियों को स्थिर फ़ील्ड हैक कर रहे हैं जो आंतरिक रूप से चेक में उपयोग किया जाता है।


7
बहुत अच्छा जवाब, एक स्वीकार किया जाना चाहिए क्योंकि यह वास्तविक समस्या को हल करता है और एक वर्कअराउंड का सुझाव नहीं देता है जो प्राप्त सर्वर पर निर्भर करता है
Feirell

1
वास्तव में यह समाधान एक आकर्षण के रूप में काम करता है, क्योंकि जो ओवरराइड संपत्ति का उपयोग करता है वह सर्वर पर निर्भर है (और मेरे मामले में काम नहीं किया गया था) ...
अमीचई अनगर

क्या यह अभी भी जावा 9 के साथ काम करता है? या मॉड्यूल बात यह प्रतिबंधित करता है
CLOVIS

2
कोशिश की कि JDK12 के साथ, लेकिन मुझे "java.lang.NoSuchFieldException: संशोधक" मिला
परिजनों ने


33

इसके लिए OpenJDK में एक W Fix Fix बग नहीं है: https://bugs.openjdk.java.net/browse/JDK-7016595

हालाँकि, Apache Http-Components क्लाइंट 4.2+ के साथ यह संभव है। इसमें एक कस्टम नेटवर्किंग कार्यान्वयन है, इस प्रकार PATCH जैसी गैर-मानक HTTP विधियों का उपयोग संभव है। यहां तक ​​कि पैच विधि का समर्थन करने वाला एक HttpPatch वर्ग भी है।

CloseableHttpClient httpClient = HttpClients.createDefault();
HttpPatch httpPatch = new HttpPatch(new URI("http://example.com"));
CloseableHttpResponse response = httpClient.execute(httpPatch);

मावेन निर्देशांक:

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.2+</version>
</dependency>

17

यदि परियोजना स्प्रिंग / ग्रेडल पर है ; निम्नलिखित समाधान कसरत करेंगे।

बिल्ड.ग्रेड के लिए, निम्न निर्भरता जोड़ें;

compile('org.apache.httpcomponents:httpclient:4.5.2')

और com @pany.project के अंदर अपने @SpringBootApplication वर्ग में निम्नलिखित बीन को परिभाषित करें;

 @Bean
 public RestTemplate restTemplate() {
  HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
  requestFactory.setReadTimeout(600000);
  requestFactory.setConnectTimeout(600000);
  return new RestTemplate(requestFactory);
 }

इस समाधान ने मेरे लिए काम किया।


स्प्रिंग देव के लिए यह सबसे साफ समाधान है: नए रेस्टेमप्लेट (नए (HttpCompordsClientHttpRequestFactory));
जॉन ट्राइब

साभार @hirosht स्प्रिंग-आधारित ऐप में इसे हल करने का सबसे साफ और सरल तरीका।
डैनियल कैमरसा

4

मेरे पास एक ही अपवाद था और सॉकेट्स समाधान (ग्रूवी में) लिखा था, लेकिन मैं उत्तर के रूप में जावा के लिए ट्रास्लेट करता हूं:

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;
    }

मुझे लगता है कि यह जावा में काम करता है। आपको सर्वर और पोर्ट नंबर को याद रखना होगा कि होस्ट हेडर को भी बदल दें और हो सकता है कि आपको कुछ अपवादों को पकड़ना पड़े।

सादर


1
मान्य नहीं है। HTTP में लाइन टर्मिनेटर के रूप में निर्दिष्ट किया गया है \r\n, न कि जो कुछ भी println()प्रदान करता है।
user207421

4

इस पोस्ट में वर्णित परावर्तन और संबंधित पोस्ट काम नहीं करता है यदि आप HttpsURLConnectionओरेकल के JRE का उपयोग कर रहे हैं , क्योंकि इसके से क्षेत्र sun.net.www.protocol.https.HttpsURLConnectionImplका उपयोग कर रहा है !methodjava.net.HttpURLConnectionDelegateHttpsURLConnection

तो एक पूर्ण कार्य समाधान है:

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);
    }
}

1
यदि आप पहले से ही प्रतिबिंब का उपयोग कर रहे हैं, तो java.net को दोबारा लिखकर "PATCH" पद्धति को क्यों न जोड़ा जाए। HttpURLConnection # विधियाँ जीवनकाल में एक बार मान लें?
okutane

अच्छी बात। हालांकि, मेरा जवाब केवल यह दिखाने के लिए है कि सुझाए गए समाधान कैसे काम करना चाहिए, न कि दूसरा समाधान दिखाने के लिए
rmuller

@okutane, क्या आप कृपया थोड़ा संकेत दे सकते हैं कि हम तरीकों को फिर से कैसे लिख सकते हैं? क्योंकि मैंने इसमें प्रतिनिधियों के बारे में बात करते हुए कुछ पोस्ट देखे हैं
कोडर

@ दमयंती मैंने इसके लिए अलग उत्तर पोस्ट किया है।
ओकुटेन

क्या आप कृपया लिंक साझा कर सकते हैं?
कोडर

2

उत्तर का उपयोग करना:

HttpURLConnection अमान्य HTTP विधि: PATCH

मैंने एक नमूना अनुरोध बनाया है और एक आकर्षण की तरह काम कर रहा हूं:

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;
}

मुझे उम्मीद है कि यह आपकी मदद करेगा।


यदि आपका सर्वर कनेक्टेड हेडर 'X-HTTP-Method-Override' को स्वीकार और व्याख्या करता है तो आपका समाधान काम करता है। इसलिए आपके समाधान का उपयोग सभी मामलों में नहीं किया जा सकता है।
इमैनुएल डिवैक्स

2

स्प्रिंग रेस्टेमप्लेट का उपयोग करने वाले किसी व्यक्ति के लिए एक विस्तृत उत्तर की तलाश में।

यदि आप SimpleClientHttpRequestFactory का उपयोग कर रहे हैं तो आपको समस्या का सामना करना पड़ेगा।

Java.net.HttpURLConnection से:

/* valid HTTP methods */
private static final String[] methods = {
    "GET", "POST", "HEAD", "OPTIONS", "PUT", "DELETE", "TRACE"
};

चूंकि PATCH एक समर्थित ऑपरेशन नहीं है, इसलिए उसी वर्ग के कोड की यह लाइन निष्पादित होगी:

throw new ProtocolException("Invalid HTTP method: " + method);

मैंने उसी का उपयोग करते हुए समाप्त किया, जैसा कि @hirosht ने अपने उत्तर में सुझाया था ।


1

एक और गंदा हैक समाधान पलटा है:

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);
 }

Http कनेक्शन के लिए काम करता है, लेकिन https के लिए नहीं। Sun.net.www.protocol.https.HttpsURLConnectionImpl वर्ग वास्तविक URL कनेक्शन वाले "प्रतिनिधि" फ़ील्ड का उपयोग करता है। इसलिए इसके बजाय वहां बदलाव होना चाहिए।
प्रति सेडर्बर्ग

0

आप एक विस्तृत समाधान पा सकते हैं, भले ही आपके पास सीधे पहुंच न हो, काम कर सकता है HttpUrlConnection (जैसे कि जर्सी ग्राहक के साथ काम करते समय: जर्सी ग्राहक के लिए अनुरोध


आपके उत्तर के लिए धन्यवाद। लेकिन आपको क्या लगता है बेहतर है, डायरेक्ट httpUrlConnection या जर्सी क्लाइंट का उपयोग करें?
डुआन ब्रेसन

0

यदि आपका सर्वर ASP.NET कोर का उपयोग कर रहा है, तो आप हेडर का उपयोग करके HTTP विधि को निर्दिष्ट करने के लिए निम्न कोड जोड़ सकते हैं X-HTTP-Method-Override, जैसा कि स्वीकृत उत्तर में वर्णित है ।

app.Use((context, next) => {
    var headers = context.Request.Headers["X-HTTP-Method-Override"];
    if(headers.Count == 1) {
        context.Request.Method = headers.First();
    }
    return next();
});

Startup.Configureअपनी कॉल करने से पहले बस इस कोड को जोड़ें app.UseMvc()


0

एपीआई 16 के एम्यूलेटर में मुझे एक अपवाद मिला: java.net.ProtocolException: Unknown method 'PATCH'; must be one of [OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE] :।

जबकि एक स्वीकृत उत्तर काम करता है, मैं एक विवरण जोड़ना चाहता हूं। नए APIs में PATCHअच्छी तरह से काम करता है, इसलिए https://github.com/OneDrive/onedrive-sdk-android/issues/16 के साथ मिलकर आपको लिखना चाहिए:

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);
}

मैं एपीआई 16, 19, 21 में परीक्षण के बाद बदल JELLY_BEAN_MR2गया KITKAT


0

मुझे जर्सी क्लाइंट के साथ मिली। वर्कअराउंड था:

Client client = ClientBuilder.newClient();
client.property(HttpUrlConnectorProvider.SET_METHOD_WORKAROUND, true);

0

हमने थोड़े अलग व्यवहार के साथ एक ही समस्या का सामना किया है। बाकी कॉल करने के लिए हम अपाचे 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
/* valid HTTP methods */
    private static final String[] methods = {
        "GET", "POST", "HEAD", "OPTIONS", "PUT", "DELETE", "TRACE"
    };

और हम देख सकते हैं कि कोई PATCH विधि परिभाषित नहीं है इसलिए त्रुटि हुई। हमने बहुत सी अलग-अलग चीजों की कोशिश की और स्टैक ओवरफ्लो देखा। एकमात्र उचित उत्तर एक और मूल्य "पैटच" को इंजेक्ट करने के लिए तरीकों के चर को संशोधित करने के लिए प्रतिबिंब का उपयोग करना था। लेकिन किसी भी तरह से हम इसका उपयोग करने के लिए आश्वस्त नहीं थे क्योंकि समाधान हैक की तरह था और बहुत अधिक काम है और इसका प्रभाव हो सकता है क्योंकि हमारे पास सभी कनेक्शन बनाने और इन REST कॉल करने के लिए सामान्य पुस्तकालय था।

लेकिन तब हमें एहसास हुआ कि cxf लाइब्रेरी खुद अपवाद को संभाल रही है और प्रतिबिंब का उपयोग करके लापता विधि को जोड़ने के लिए कैच ब्लॉक में कोड लिखा है।

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 बना सकते हैं और हमारा काम वैसा ही होगा जैसा कि डिफ़ॉल्ट रूप से होगा। नीचे दिए गए कोड के कारण गलत को सौंपा गया

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 लाइब्रेरी से ही है।

आशा है कि यह उत्तर कुछ मदद करता है।


0
 **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()));**

मावेन प्लगइन


> <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>

वास्तव में इसका उपयोग करें इससे आपको मदद मिलेगी


0

जावा 11+ में आप जो चाहते हैं उसे करने के लिए HttpRequest वर्ग का उपयोग कर सकते हैं:

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();
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.