Java 使用 Jersey 客户端的 PATCH 请求
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/22355235/
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
PATCH request using Jersey Client
提问by nilesh
I want to execute a PATCH request supported by our server for testing using Jersey client. My code is as below, but I get com.sun.jersey.api.client.ClientHandlerException: java.net.ProtocolException: HTTP method PATCH doesn't support output
exception. Can someone let me know whats wrong with the code below?
我想执行我们的服务器支持的 PATCH 请求,以便使用 Jersey 客户端进行测试。我的代码如下,但出现com.sun.jersey.api.client.ClientHandlerException: java.net.ProtocolException: HTTP method PATCH doesn't support output
异常。有人可以让我知道下面的代码有什么问题吗?
String complete_url = "http://localhost:8080/api/request";
String request = "[{\"op\":\"add\", \"path\":\"/name\", \"value\":\"Hello\"}]";
DefaultClientConfig config = new DefaultClientConfig();
config.getProperties().put(URLConnectionClientHandler.PROPERTY_HTTP_URL_CONNECTION_SET_METHOD_WORKAROUND, true);
Client client = Client.create(config);
WebResource resource = client.resource(complete_url);
ClientResponse response = resource.header("Authorization", "Basic xyzabCDef")
.type(new MediaType("application", "json-patch+json"))
.method("PATCH", ClientResponse.class, request);
Here is the full exception,
这是完整的例外,
com.sun.jersey.api.client.ClientHandlerException: java.net.ProtocolException: HTTP method PATCH doesn't support output
at com.sun.jersey.client.urlconnection.URLConnectionClientHandler.handle(URLConnectionClientHandler.java:155)
at com.sun.jersey.api.client.Client.handle(Client.java:652)
at com.sun.jersey.api.client.WebResource.handle(WebResource.java:682)
at com.sun.jersey.api.client.WebResource.access0(WebResource.java:74)
at com.sun.jersey.api.client.WebResource$Builder.method(WebResource.java:634)
at com.acceptance.common.PatchTest.patch(PatchTest.java:42)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.junit.runners.model.FrameworkMethod.runReflectiveCall(FrameworkMethod.java:47)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
at org.junit.runners.ParentRunner.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access Client jerseyClient = ClientBuilder.newClient()
.property(HttpUrlConnectorProvider.SET_METHOD_WORKAROUND, true)
... etc ...
0(ParentRunner.java:53)
at org.junit.runners.ParentRunner.evaluate(ParentRunner.java:229)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Caused by: java.net.ProtocolException: HTTP method PATCH doesn't support output
at sun.net.www.protocol.http.HttpURLConnection.getOutputStream(HttpURLConnection.java:1021)
at com.sun.jersey.client.urlconnection.URLConnectionClientHandler.getOutputStream(URLConnectionClientHandler.java:238)
at com.sun.jersey.api.client.CommittingOutputStream.commitStream(CommittingOutputStream.java:117)
at com.sun.jersey.api.client.CommittingOutputStream.write(CommittingOutputStream.java:89)
at sun.nio.cs.StreamEncoder.writeBytes(StreamEncoder.java:202)
at sun.nio.cs.StreamEncoder.implFlushBuffer(StreamEncoder.java:272)
at sun.nio.cs.StreamEncoder.implFlush(StreamEncoder.java:276)
at sun.nio.cs.StreamEncoder.flush(StreamEncoder.java:122)
at java.io.OutputStreamWriter.flush(OutputStreamWriter.java:212)
at java.io.BufferedWriter.flush(BufferedWriter.java:236)
at com.sun.jersey.core.util.ReaderWriter.writeToAsString(ReaderWriter.java:191)
at com.sun.jersey.core.provider.AbstractMessageReaderWriterProvider.writeToAsString(AbstractMessageReaderWriterProvider.java:128)
at com.sun.jersey.core.impl.provider.entity.StringProvider.writeTo(StringProvider.java:88)
at com.sun.jersey.core.impl.provider.entity.StringProvider.writeTo(StringProvider.java:58)
at com.sun.jersey.api.client.RequestWriter.writeRequestEntity(RequestWriter.java:300)
at com.sun.jersey.client.urlconnection.URLConnectionClientHandler._invoke(URLConnectionClientHandler.java:217)
at com.sun.jersey.client.urlconnection.URLConnectionClientHandler.handle(URLConnectionClientHandler.java:153)
采纳答案by Abhijeet Kushe
This is a bug in the current JDK implementation which has been fixed in the JDK8 implementation.Checkout this link for details https://bugs.openjdk.java.net/browse/JDK-7157360. There is a way to hack around this but Jersey team decided not to fix it https://github.com/eclipse-ee4j/jersey/issues/1639
这是当前 JDK 实现中的一个错误,已在 JDK8 实现中修复。查看此链接了解详细信息https://bugs.openjdk.java.net/browse/JDK-7157360。有一种方法可以解决这个问题,但 Jersey 团队决定不修复它https://github.com/eclipse-ee4j/jersey/issues/1639
2 solutions which I can think of
我能想到的2个解决方案
- use Apache Http Client which supports HttpPatch method
- use Jersey Client PostReplaceFilter but the Container code has to be modified and include X-HTTP-Method-Override header with value as PATCH while making a post request. Refer to http://zcox.wordpress.com/2009/06/17/override-the-http-request-method-in-jersey/]
- 使用支持 HttpPatch 方法的 Apache Http Client
- 使用 Jersey 客户端 PostReplaceFilter 但必须修改容器代码,并在发出发布请求时包含 X-HTTP-Method-Override 标头,其值为 PATCH。参考http://zcox.wordpress.com/2009/06/17/override-the-http-request-method-in-jersey/]
回答by Kevin Day
FYI - just in case anyone runs into this in Jersey 2, see:
仅供参考 - 以防万一有人在 Jersey 2 中遇到此问题,请参阅:
and use the SET_METHOD_WORKAROUND property as follows:
并使用 SET_METHOD_WORKAROUND 属性如下:
/**
* Workaround for a bug in {@code HttpURLConnection.setRequestMethod(String)}
* The implementation of Sun/Oracle is throwing a {@code ProtocolException}
* when the method is other than the HTTP/1.1 default methods. So to use {@code PROPFIND}
* and others, we must apply this workaround.
*
* See issue http://java.net/jira/browse/JERSEY-639
*/
private static void setRequestMethodViaJreBugWorkaround(final HttpURLConnection httpURLConnection, final String method) {
try {
httpURLConnection.setRequestMethod(method); // Check whether we are running on a buggy JRE
} catch (final ProtocolException pe) {
try {
final Class<?> httpURLConnectionClass = httpURLConnection.getClass();
AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
@Override
public Object run() throws NoSuchFieldException, IllegalAccessException {
final Field methodField = httpURLConnectionClass.getSuperclass().getDeclaredField("method");
methodField.setAccessible(true);
methodField.set(httpURLConnection, method);
return null;
}
});
} catch (final PrivilegedActionException e) {
final Throwable cause = e.getCause();
if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
} else {
throw new RuntimeException(cause);
}
}
}
}
Took me forever to find this - figured I could help short circuit the learning curve for others.
我花了很长时间才找到这个 - 认为我可以帮助缩短其他人的学习曲线。
回答by Daniel L.
If you're using HttpsUrlConnection
(note the 's') - then setting the HttpUrlConnectorProvider.SET_METHOD_WORKAROUND
won't work. Keep reading for a detailed solution.
如果您正在使用HttpsUrlConnection
(注意's') - 那么设置HttpUrlConnectorProvider.SET_METHOD_WORKAROUND
将不起作用。继续阅读以获取详细的解决方案。
In my case, setting HttpUrlConnectorProvider.SET_METHOD_WORKAROUND
property caused a NoSuchFieldException
since my HttpUrlConnection
instance was actually of type: sun.net.www.protocol.https.HttpsURLConnectionImpl
and it's super: javax.net.ssl.HttpsURLConnection
(which inherits from HttpUrlConnection
).
在我的情况下,设置HttpUrlConnectorProvider.SET_METHOD_WORKAROUND
属性导致了 aNoSuchFieldException
因为我的HttpUrlConnection
实例实际上是 type:sun.net.www.protocol.https.HttpsURLConnectionImpl
并且它是 super:(javax.net.ssl.HttpsURLConnection
继承自HttpUrlConnection
)。
So when Hymanson code try to get the method field from my connection instance super (instance of javax.net.ssl.HttpsURLConnection
) here:
因此,当 Hymanson 代码尝试从我的连接实例 super(实例javax.net.ssl.HttpsURLConnection
)中获取方法字段时:
/* Adding PATCH to the valid HTTP methods */
private static final String[] methods = {
"GET", "POST", "HEAD", "OPTIONS", "PUT", "DELETE", "TRACE"
};
We get a NoSuchFieldException
stating that a field named method
doesn't exist (since getDeclaredFields() brings all the fields, regardless of their accessibility but only for the current class, not any base classes that the current class might be inheriting from).
我们得到一个NoSuchFieldException
声明,一个名为的字段method
不存在(因为 getDeclaredFields() 带来了所有字段,无论它们的可访问性如何,但仅适用于当前类,而不是当前类可能继承的任何基类)。
So I looked into Java's HttpUrlConnection code and saw that the allowed methods are specified by an private staticString[]:
所以我查看了 Java 的 HttpUrlConnection 代码,发现允许的方法是由私有静态String[]指定的:
try {
Field methodsField = HttpURLConnection.class.getDeclaredField("methods");
methodsField.setAccessible(true);
// get the methods field modifiers
Field modifiersField = Field.class.getDeclaredField("modifiers");
// bypass the "private" modifier
modifiersField.setAccessible(true);
// remove the "final" modifier
modifiersField.setInt(methodsField, methodsField.getModifiers() & ~Modifier.FINAL);
/* valid HTTP methods */
String[] methods = {
"GET", "POST", "HEAD", "OPTIONS", "PUT", "DELETE", "TRACE", "PATCH"
};
// set the new methods - including patch
methodsField.set(null, methods);
} catch (SecurityException | IllegalArgumentException | IllegalAccessException | NoSuchFieldException e) {
e.printStackTrace();
}
The solution was to change this methods array using reflection:
解决方案是使用反射更改此方法数组:
<!-- https://mvnrepository.com/artifact/org.glassfish.jersey.core/jersey-client -->
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-client</artifactId>
<version>2.27</version>
</dependency>
Since methods field is static, changing its value works for any concrete instance that is extending HttpUrlConnection
including HttpsUrlConnection
.
由于 methods 字段是静态的,更改其值适用于任何扩展HttpUrlConnection
包括HttpsUrlConnection
.
Side note: I would prefer Java to add the PATCH method to the JDK or from Hymanson to perform the field look up through the entire hierarchy in their workaround.
旁注:我更喜欢 Java 将 PATCH 方法添加到 JDK 或从 Hymanson 添加到其解决方法中的整个层次结构中的字段查找。
Anyway, I hope this solution will save you some time.
无论如何,我希望这个解决方案可以为您节省一些时间。
回答by NarendraR
Easy answer would be :
简单的答案是:
Dependencies
依赖关系
Client client = ClientBuilder.newClient(clientConfig).property(HttpUrlConnectorProvider.SET_METHOD_WORKAROUND, true);
Need to add HttpUrlConnectorProvider.SET_METHOD_WORKAROUND
=true
需要加HttpUrlConnectorProvider.SET_METHOD_WORKAROUND
=true
client.target("base_url").path("end_point").request().headers("your_headers")
.build("PATCH", Entity.entity("body_you_want_to_pass", "content-type"))
.invoke();
Request
要求
##代码##