sourcecode

HttpURL Connection HTTP 메서드: PATCH

copyscript 2022. 9. 19. 23:30
반응형

HttpURL Connection HTTP 메서드: PATCH

URL Connection을 사용한PATCH 등의 비표준 HTTP 메서드를 사용하려고 하면 다음과 같이 됩니다.

    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 요구를 발행하기 위한 회피책이 있습니까?

좋은 답변이 많이 있습니다(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);
        }
    }
}

반사도 이용하지만 모든 연결 객체를 해킹하는 대신 Http를 해킹하고 있습니다.URL Connection # 메서드스태틱필드는 내부적으로 사용됩니다.

네, 이에 대한 회피책이 있습니다.사용하다

X-HTTP-Method-Override

이 헤더는 다른 HTTP 메서드를 "위장"하기 위해 POST 요청에서 사용할 수 있습니다.X-HTTP-Method-Override 헤더의 값을 실제로 실행하는HTTP 메서드로 설정하기만 하면 됩니다.다음 코드를 사용합니다.

conn.setRequestProperty("X-HTTP-Method-Override", "PATCH");
conn.setRequestMethod("POST");

OpenJDK에는 다음 버그가 있습니다.https://bugs.openjdk.java.net/browse/JDK-7016595

그러나 Apache Http-Components Client 4.2+를 사용하면 가능합니다.커스텀 네트워킹 실장이 있기 때문에 PATCH 등의 모든 표준HTTP 메서드를 사용할 수 있습니다.패치 방식을 지원하는 HttpPatch 클래스도 있습니다.

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

메이븐 좌표:

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.2+</version>
</dependency>

프로젝트가 Spring/Gradle에 있는 경우 다음과 같은 솔루션이 효과적입니다.

build.gradle에 다음 종속성을 추가합니다.

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

com.company.프로젝트 내의 @SpringBootApplication 클래스에서 다음 콩을 정의합니다.

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

이 솔루션은 나에게 효과가 있었다.

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();

이 투고 및 관련 투고에 기재되어 있는 리플렉션은, 다음의 기능을 사용하고 있는 경우는 동작하지 않습니다.HttpsURLConnectionJRE에 합니다. 왜냐하면 JRE는왜냐하면sun.net.www.protocol.https.HttpsURLConnectionImpl요.method の の の fromjava.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);
    }
}

답변 사용:

HttpURL Connection HTTP 메서드: PATCH

샘플 요청서를 작성해서 매력적으로 작업합니다.

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

도움이 됐으면 좋겠네요.

같은 예외로 소켓솔루션(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;
    }

자바에서 작동하는 것 같아요.서버와 포트 번호를 변경해야 합니다.호스트 헤더도 변경해야 합니다.또, 몇개의 예외를 검출할 필요가 있는 경우도 있습니다.

Spring restTemplate를 사용하는 모든 사용자에게 자세한 답변을 원하시면 됩니다.

restTemplate의 ClientHttpRequestFactory로 SimpleClientHttpRequestFactory를 사용하는 경우 문제가 발생합니다.

java.net 에서.HttpURL 연결:

/* valid HTTP methods */
private static final String[] methods = {
    "GET", "POST", "HEAD", "OPTIONS", "PUT", "DELETE", "TRACE"
};

PATCH 는 서포트되고 있지 않기 때문에, 같은 클래스의 다음의 코드 행이 실행됩니다.

throw new ProtocolException("Invalid HTTP method: " + method);

저는 @hirosht가 의 답변에서 제시한 것과 같은 것을 사용하게 되었습니다.

또 다른 지저분한 해킹 솔루션은 리플렉션입니다.

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

직접 액세스 할 수 없는 경우에도 동작할 수 있는 상세한 솔루션을 찾을 수 있습니다.HttpUrlConnection(여기서 Jersey Client를 사용하는 경우: Jersey Client를 사용한 패치 요청 등)

서버가 ASP를 사용하는 경우.NET Core, 다음 코드를 추가하여 헤더를 사용하여 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().

API 16의 에뮬레이터에서 예외를 받았습니다.java.net.ProtocolException: Unknown method 'PATCH'; must be one of [OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE].

인정된 답변은 유효하지만, 한 가지 세부사항을 추가하고 싶습니다.새로운 API에서PATCHhttps://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로.KITKATAPI 16, 19, 21에서 테스트한 후.

Jersey 고객한테 받았어요회피책은 다음과 같습니다.

Client client = ClientBuilder.newClient();
client.property(HttpUrlConnectorProvider.SET_METHOD_WORKAROUND, true);

우리는 약간 다른 행동으로 같은 문제에 직면해 왔다.나머지 통화는 아파치 cxf 라이브러리를 사용했어요우리는 http를 통해 동작하는 가짜 서비스와 대화할 때까지 PATCH는 정상적으로 동작하고 있었습니다.실제 시스템(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"를 주입하는 것이었습니다.하지만 이 솔루션을 사용하는 것은 납득할 수 없었습니다.솔루션이 해킹이고 작업이 너무 많고 모든 접속과 REST 호출을 실행할 수 있는 공통 라이브러리가 있었기 때문입니다.

그러나 그 후 우리는 cxf 라이브러리 자체가 예외를 처리하고 있으며 catch block에 reflection을 사용하여 누락된 메서드를 추가하는 코드가 있다는 것을 알게 되었습니다.

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

이것은 우리에게 약간의 희망을 주었습니다.그래서 우리는 코드를 읽는 데 시간을 할애하여 URL Connection의 속성을 제공한다면HTTPConduit.HTTPURL_CONNECTION_METOD_REFLISION을 사용하여 예외 핸들러를 실행할 수 있습니다.이 경우 아래 코드로 인해 변수가 false로 할당됩니다.

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 라이브러리 자체에서 가져옵니다.

이 답변이 누군가에게 도움이 되길 바랍니다.

 **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>

이걸 쓰면 도움이 될 거야

언급URL : https://stackoverflow.com/questions/25163131/httpurlconnection-invalid-http-method-patch

반응형