[java] HttpURLConnection 잘못된 HTTP 메서드 : PATCH

URLConnection에서 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 요청을 발행하는 해결 방법이 있습니까?



답변

예, 이에 대한 해결 방법이 있습니다. 사용하다

X-HTTP-Method-Override

. 이 헤더는 POST 요청에서 다른 HTTP 메서드를 “위조”하는 데 사용할 수 있습니다. X-HTTP-Method-Override 헤더의 값을 실제로 수행하려는 HTTP 메서드로 설정하기 만하면됩니다. 따라서 다음 코드를 사용하십시오.

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


답변

좋은 답변이 많이 있으므로 여기에 내 것입니다 (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 정적 필드를 해킹합니다.


답변

이에 대한 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);

Maven 좌표 :

<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.project 내의 @SpringBootApplication 클래스에 다음 빈을 정의하십시오.

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

이 솔루션은 저에게 효과적이었습니다.


답변

나는 같은 예외가 있었고 소켓 솔루션 (그루비)을 썼지 만 당신을 위해 자바에 대한 대답 양식을 번역했습니다.

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

나는 그것이 자바에서 작동한다고 생각합니다. 서버와 포트 번호를 변경해야합니다. 호스트 헤더도 변경해야하며 예외를 잡아야 할 수도 있습니다.

친애하는


답변

반영이 게시물에 설명과 같이 관련 게시물 당신이 사용하는 경우 작동하지 않습니다 HttpsURLConnection오라클의 JRE에를하기 때문에, 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);
    }
}


답변

답변 사용 :

HttpURLConnection 잘못된 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;
}

도움이 되었기를 바랍니다.