HttpURLConnection无效的HTTP方法:PATCH

kav*_*i77 64 java httpurlconnection

当我尝试使用非标准的HTTP方法,如PATCH和URLConnection:

    HttpURLConnection conn = (HttpURLConnection) new URL("http://example.com").openConnection();
    conn.setRequestMethod("PATCH");
Run Code Online (Sandbox Code Playgroud)

我得到一个例外:

java.net.ProtocolException: Invalid HTTP method: PATCH
at java.net.HttpURLConnection.setRequestMethod(HttpURLConnection.java:440)
Run Code Online (Sandbox Code Playgroud)

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

sag*_*gar 44

是的,有解决方法.使用

X-HTTP-方法,覆盖

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

conn.setRequestProperty("X-HTTP-Method-Override", "PATCH");
conn.setRequestMethod("POST");
Run Code Online (Sandbox Code Playgroud)

  • 这仅在接收端支持时才有效.它仍然发送"POST". (27认同)
  • 这不是一个有效的答案,因为它没有解决 javas 方面的问题。服务器必须允许您使用“POST”并且必须了解“X-HTTP-Method-Override”字段。请参阅 /sf/answers/3242672401/ 以获得更好的实际修复 (6认同)
  • 使用HttpUrlConnection调用Firebase REST API时,此方法有效. (4认同)
  • 如果接收器支持它,那么(对我而言)它是最干净的方式. (3认同)

oku*_*ane 32

有很多好的答案,所以这是我的:

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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

它也使用反射,但不是黑客攻击每个连接对象,而是黑客攻击HttpURLConnection#方法静态字段,它在内部检查中使用.

  • 非常好的答案,应该是被接受的答案,因为它解决了实际问题,并没有建议取决于接收服务器的解决方法 (5认同)
  • 在JDK12上进行了尝试,但得到了“ java.lang.NoSuchFieldException:修饰符” (2认同)
  • 原因:/sf/ask/3922753901/ (2认同)

kav*_*i77 29

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

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

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

Maven坐标:

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.2+</version>
</dependency>
Run Code Online (Sandbox Code Playgroud)

  • 为什么说Patch是非标准的呢?这是标准的重要组成部分...... (2认同)

sim*_*005 15

在 java 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();
Run Code Online (Sandbox Code Playgroud)


rmu*_*ler 7

如果您在 Oracle 的 JRE 上使用 a ,则本文和相关帖子中所述的反射不起作用HttpsURLConnection,因为sun.net.www.protocol.https.HttpsURLConnectionImpl使用的是method来自java.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);
    }
}
Run Code Online (Sandbox Code Playgroud)


hir*_*sht 7

如果项目在Spring / Gradle上;以下解决方案将进行锻炼。

对于build.gradle,添加以下依赖项;

compile('org.apache.httpcomponents:httpclient:4.5.2')
Run Code Online (Sandbox Code Playgroud)

并在com.company.project中的@SpringBootApplication类中定义以下bean;

 @Bean
 public RestTemplate restTemplate() {
  HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
  requestFactory.setReadTimeout(600000);
  requestFactory.setConnectTimeout(600000);
  return new RestTemplate(requestFactory);
 }
Run Code Online (Sandbox Code Playgroud)

该解决方案为我工作。