Java HttpURLConnection 无效的 HTTP 方法:PATCH

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/25163131/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 23:45:41  来源:igfitidea点击:

HttpURLConnection Invalid HTTP method: PATCH

javahttpurlconnection

提问by kavai77

When I try to use a non-standard HTTP Method like PATCH with URLConnection:

当我尝试使用非标准 HTTP 方法(如 PATCH 和 URLConnection)时:

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

I get an exception:

我得到一个例外:

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

Using a higher level API like Jersey generates the same error. Is there a workaround to issue a PATCH HTTP request?

使用像 Jersey 这样的更高级别的 API 会产生相同的错误。是否有解决方法来发出 PATCH HTTP 请求?

采纳答案by sagar

Yes there is workaround for this. Use

是的,有解决方法。用

X-HTTP-Method-Override

X-HTTP-方法-覆盖

. This header can be used in a POST request to “fake” other HTTP methods. Simply set the value of the X-HTTP-Method-Override header to the HTTP method you would like to actually perform. So use following code.

. 此标头可用于 POST 请求以“伪造”其他 HTTP 方法。只需将 X-HTTP-Method-Override 标头的值设置为您想要实际执行的 HTTP 方法。所以使用下面的代码。

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

回答by kavai77

There is a Won't Fix bug in OpenJDK for this: https://bugs.openjdk.java.net/browse/JDK-7016595

OpenJDK 中有一个不会修复的错误:https: //bugs.openjdk.java.net/browse/JDK-7016595

However, with Apache Http-Components Client 4.2+ this is possible. It has a custom networking implementation, thus using non-standard HTTP methods like PATCH is possible. It even has a HttpPatch class supporting the patch method.

但是,使用 Apache Http-Components Client 4.2+,这是可能的。它有一个自定义的网络实现,因此可以使用非标准的 HTTP 方法,如 PATCH。它甚至有一个支持 patch 方法的 HttpPatch 类。

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

Maven Coordinates:

马文坐标:

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

回答by moralejaSinCuentoNiProverbio

I had the same exception and wrote sockets solution (in groovy) but i traslate in the answer form to java for you:

我有同样的例外并编写了套接字解决方案(在 groovy 中),但我在答案中为您翻译了 java:

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

I think it work in java. You have to change the server and port number remember change the Host header too and Maybe you have to catch some exception.

我认为它在java中工作。您必须更改服务器和端口号,记住也要更改 Host 标头,也许您必须捕获一些异常。

Best Regards

此致

回答by dreamtangerine

Another dirty hack solution is 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);
 }

回答by Daniel L.

You can find a detailed solution that can work even if you don't have direct access to the HttpUrlConnection(like when working with Jersey Client here: PATCH request using Jersey Client

您可以找到一个详细的解决方案,即使您没有直接访问权限HttpUrlConnection(例如在此处使用 Jersey Client 时PATCH request using Jersey Client

回答by rmuller

Reflection as described in this post and a related postdoes not work if you are using a HttpsURLConnectionon Oracle's JRE, becausesun.net.www.protocol.https.HttpsURLConnectionImplis using the methodfield from the java.net.HttpURLConnectionof its DelegateHttpsURLConnection!

如果您在 Oracle 的 JRE 上使用 a ,则本文和相关帖子中描述的反射不起作用HttpsURLConnection,因为sun.net.www.protocol.https.HttpsURLConnectionImpl使用的是method来自java.net.HttpURLConnectionDelegateHttpsURLConnection!

So a complete workingsolution is:

所以一个完整的工作解决方案是:

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

回答by Peppe L-G

If your server is using ASP.NET Core, you can simply add the following code to specify the HTTP method using the header X-HTTP-Method-Override, as described in the accepted answer.

如果您的服务器使用 ASP.NET Core,您可以简单地添加以下代码以使用 header 指定 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();
});

Simply add this code in Startup.Configurebefore your call to app.UseMvc().

只需在Startup.Configure调用app.UseMvc().

回答by Duan Bressan

Using the answer:

使用答案:

HttpURLConnection Invalid HTTP method: PATCH

HttpURLConnection 无效的 HTTP 方法:PATCH

I'm created a sample request and work like a charm:

我创建了一个示例请求并像魅力一样工作:

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

I hope it help you.

我希望它能帮助你。

回答by CoolMind

In emulator of API 16 I received an exception: java.net.ProtocolException: Unknown method 'PATCH'; must be one of [OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE].

在 API 16 的模拟器中,我收到一个异常:java.net.ProtocolException: Unknown method 'PATCH'; must be one of [OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE].

While an accepted answer works, I want to add one detail. In new APIs PATCHworks well, so in conjunction with https://github.com/OneDrive/onedrive-sdk-android/issues/16you should write:

虽然接受的答案有效,但我想添加一个细节。在新的 API 中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);
}

I changed JELLY_BEAN_MR2to KITKATafter testing in API 16, 19, 21.

我在 API 16、19、21 中测试后更改JELLY_BEAN_MR2KITKAT

回答by okutane

There are a lot of good answers, so here is mine (not work in jdk12):

有很多好的答案,所以这是我的(不适用于 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);
        }
    }
}

It also uses reflection, but instead of hacking into every connection object we're hacking HttpURLConnection#methods static field which is used in the checks internally.

它还使用反射,但我们不是侵入每个连接对象,而是侵入内部检查中使用的 HttpURLConnection#methods 静态字段。