当我尝试使用非标准的HTTP方法,如PATCH和URLConnection:
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)
使用像Jersey这样的更高级别的API会产生相同的错误。是否有解决方法来发出PATCH HTTP请求?
答案 0 :(得分:26)
有很多好的答案,所以这是我的:
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#方法静态字段。
答案 1 :(得分:25)
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);
Maven坐标:
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.2+</version>
</dependency>
答案 2 :(得分:4)
我有相同的异常,并编写了套接字解决方案(在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;
}
我认为它适用于java。您必须更改服务器和端口号,记住也要更改Host标头,也许您必须捕获一些异常。
最好的问候
答案 3 :(得分:4)
如果您在Oracle的JRE上使用HttpsURLConnection
,则此帖子和related post中描述的反映不起作用,因为sun.net.www.protocol.https.HttpsURLConnectionImpl
正在使用method
来自java.net.HttpURLConnection
的{{1}}的字段!
完整的工作解决方案是:
DelegateHttpsURLConnection
答案 4 :(得分:2)
使用答案:
我创建了一个示例请求并且像魅力一样工作:
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;
}
我希望它可以帮到你。
答案 5 :(得分:2)
如果项目在Spring / Gradle上;以下解决方案将进行锻炼。
对于build.gradle,添加以下依赖项;
import d3plus from 'npm:d3plus';
在你的@SpringBootApplication类中定义以下bean com.company.project;
compile('org.apache.httpcomponents:httpclient:4.5.2')
这个解决方案对我有用。
答案 6 :(得分:1)
我们遇到了同样的问题,行为略有不同。我们使用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
/* valid HTTP methods */
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) {
//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执行异常处理程序,我们的工作完成为默认情况下由于下面的代码
,变量将被赋值为falseDEFAULT_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库本身。
希望这个答案有所帮助。
答案 7 :(得分:0)
另一个肮脏的黑客解决方案是反思:
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);
}
答案 8 :(得分:0)
即使您无法直接访问HttpUrlConnection
,也可以找到可行的详细解决方案(例如在此处使用Jersey客户端时PATCH request using Jersey Client
答案 9 :(得分:0)
如果您的服务器使用的是ASP.NET Core,则只需添加以下代码即可使用标头X-HTTP-Method-Override
指定HTTP方法,如accepted answer中所述。
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()
中添加此代码。
答案 10 :(得分:0)
在API 16的模拟器中,我收到了一个例外:java.net.ProtocolException: Unknown method 'PATCH'; must be one of [OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE]
。
虽然接受的答案有效,但我想添加一个细节。在新的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);
}
在API 16,19,21中测试后,我将JELLY_BEAN_MR2
更改为KITKAT
。
答案 11 :(得分:0)
我和泽西客户一起得到了我的。 解决方法是:
Client client = ClientBuilder.newClient();
client.property(HttpUrlConnectorProvider.SET_METHOD_WORKAROUND, true);
答案 12 :(得分: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()));**
maven插件
> <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>
真的可以使用它
答案 13 :(得分:0)
对于任何使用Spring restTemplate寻找详细答案的人。
如果将SimpleClientHttpRequestFactory用作restTemplate的ClientHttpRequestFactory,则会遇到问题。
来自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在他的answer中建议的相同的内容。
答案 14 :(得分:0)
在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();