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

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

PATCH request using Jersey Client

javajerseyjersey-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 outputexception. 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个解决方案

  1. use Apache Http Client which supports HttpPatch method
  2. 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/]
  1. 使用支持 HttpPatch 方法的 Apache Http Client
  2. 使用 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 中遇到此问题,请参阅:

https://jersey.github.io/apidocs/latest/jersey/org/glassfish/jersey/client/HttpUrlConnectorProvider.html

https://jersey.github.io/apidocs/latest/jersey/org/glassfish/jersey/client/HttpUrlConnectorProvider.html

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_WORKAROUNDwon't work. Keep reading for a detailed solution.

如果您正在使用HttpsUrlConnection(注意's') - 那么设置HttpUrlConnectorProvider.SET_METHOD_WORKAROUND将不起作用。继续阅读以获取详细的解决方案。

In my case, setting HttpUrlConnectorProvider.SET_METHOD_WORKAROUNDproperty caused a NoSuchFieldExceptionsince my HttpUrlConnectioninstance was actually of type: sun.net.www.protocol.https.HttpsURLConnectionImpland 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 NoSuchFieldExceptionstating that a field named methoddoesn'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 HttpUrlConnectionincluding 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

要求

##代码##