HttpURLConnection Недопустимый метод HTTP: ПАТЧ

Когда я пытаюсь использовать нестандартный Метод HTTP как ПАТЧ с 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)

Используя высокоуровневый API как Джерси генерирует ту же ошибку. Существует ли обходное решение для издания Запроса HTTP ПАТЧА?

62
задан 6 August 2014 в 18:32

13 ответов

Да существует обходное решение для этого. Используйте

X-HTTP-Method-Override

. Этот заголовок может привыкнуть в запросе POST к “fake” другие методы HTTP. Просто установите значение X-HTTP-Method-Override заголовка к методу HTTP, который требуется на самом деле выполнить. Поэтому используйте следующий код.

conn.setRequestProperty("X-HTTP-Method-Override", "PATCH");
conn.setRequestMethod("POST");
44
ответ дан 31 October 2019 в 13:56
 **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()));**

плагин знатока

<час>
> <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>
<час>

использование это действительно, это было бы, помогает Вам

0
ответ дан 31 October 2019 в 13:56

Я получил мой с клиентом Джерси. Обходное решение было:

Client client = ClientBuilder.newClient();
client.property(HttpUrlConnectorProvider.SET_METHOD_WORKAROUND, true);
0
ответ дан 31 October 2019 в 13:56

В эмуляторе 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);
}

я изменился JELLY_BEAN_MR2 на KITKAT после тестирования в API 16, 19, 21.

0
ответ дан 31 October 2019 в 13:56

Если Ваш сервер использует Ядро ASP.NET, можно просто добавить следующий код для определения метода 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();
});

Просто добавляют этот код в Startup.Configure перед Вашим вызовом к app.UseMvc().

0
ответ дан 31 October 2019 в 13:56

Используя ответ:

HttpURLConnection Недопустимый метод HTTP: ПАТЧ

я создаюсь демонстрационный запрос и работа как очарование:

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

я надеюсь, что это помогает Вам.

1
ответ дан 31 October 2019 в 13:56

Отражение, как описано в этом сообщении и связанное сообщение не работает, если Вы используете HttpsURLConnection на JRE Oracle, потому что sun.net.www.protocol.https.HttpsURLConnectionImpl использует method поле от java.net.HttpURLConnection из DelegateHttpsURLConnection!

Так полное работа решение:

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);
    }
}
4
ответ дан 31 October 2019 в 13:56

, Если проект находится на Spring/Gradle; следующее решение будет тренировка.

Для build.gradle, добавьте следующую зависимость;

compile('org.apache.httpcomponents:httpclient:4.5.2')

И определяют следующий боб в Вашем @SpringBootApplication классе в com.company.project;

 @Bean
 public RestTemplate restTemplate() {
  HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
  requestFactory.setReadTimeout(600000);
  requestFactory.setConnectTimeout(600000);
  return new RestTemplate(requestFactory);
 }

Это решения работали на меня.

7
ответ дан 31 October 2019 в 13:56

Существует много хороших ответов, таким образом, здесь является моим (не, работают в 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);
        }
    }
}

Это также использует отражение, но вместо того, чтобы взломать каждое соединение возражают, что мы взламываем статическое поле HttpURLConnection#methods, которое используется в проверках внутренне.

30
ответ дан 31 October 2019 в 13:56

Мы столкнулись с той же проблемой с немного отличающимся поведением. Мы пользовались апачской cxf библиотекой для того, чтобы сделать остальных вызовами. Для нас хорошо работал ПАТЧ, пока мы не говорили с нашими поддельными сервисами, которые работали по 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"
    };

И мы видим, что нет никакого метода ПАТЧА, определенного следовательно, ошибка имела смысл. Мы попробовали большую другую вещь и просмотрели переполнение стека. Единственный разумный ответ должен был использовать отражение для изменения переменной методов для введения другого значения "ПАТЧ". Но так или иначе мы не были убеждены использовать это, поскольку решение было видом взлома и является слишком большой работой и могло бы оказать влияние, поскольку у нас была общая библиотека для устанавливания всей связи и выполняющий эти вызовы REST.

, Но затем мы поняли, что сама cxf библиотека обрабатывает исключение и существует код, написанный в блоке выгоды для добавления отсутствующего метода с помощью отражения.

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 для выполнения обработчика исключений и нашей работы, сделан как по умолчанию, переменная будет присвоена лжи из-за ниже кода

DEFAULT_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 библиотеки.

Hope этот ответ помогает кому-то.

1
ответ дан 31 October 2019 в 13:56

Можно найти подробное решение, которое может работать, даже если у Вас нет прямого доступа к HttpUrlConnection (как при работе с Клиентом Джерси здесь: запрос ПАТЧА с помощью Клиента Джерси

0
ответ дан 31 October 2019 в 13:56

Другим грязным решением для взлома является отражение:

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);
 }
0
ответ дан 31 October 2019 в 13:56

Я имел то же исключение и записал решение для сокетов (в отличном), но я traslate в ответе формируюсь к 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. Необходимо изменить сервер, и номер порта помнят, изменяют заголовок Хоста также, и Возможно необходимо поймать некоторое исключение.

Наилучшие пожелания

4
ответ дан 31 October 2019 в 13:56

Другие вопросы по тегам:

Похожие вопросы: