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");
이에 대한 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>
좋은 답변이 많이 있으므로 여기에 내 것입니다 (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 정적 필드를 해킹합니다.
프로젝트가 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);
}
}
답변 사용 :
샘플 요청을 만들고 매력처럼 작동합니다.
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;
}
도움이 되었기를 바랍니다.
우리는 약간 다른 행동으로 같은 문제에 직면했습니다. 나머지 호출을 위해 apache cxf 라이브러리를 사용했습니다. 우리에게 PATCH는 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"
};
그리고 정의 된 PATCH 메서드가 없으므로 오류가 의미가 있음을 알 수 있습니다. 우리는 많은 다른 것을 시도하고 스택 오버플로를 살펴 보았습니다. 유일한 합리적인 대답은 리플렉션을 사용하여 다른 값 "PATCH"를 주입하도록 메서드 변수를 수정하는 것입니다. 그러나 어떻게 든 우리는 솔루션이 일종의 해킹이고 너무 많은 작업이며 모든 연결을 만들고 이러한 REST 호출을 수행하는 공통 라이브러리가 있었기 때문에 영향을 미칠 수 있기 때문에 그것을 사용하도록 확신하지 못했습니다.
그러나 우리는 cxf 라이브러리 자체가 예외를 처리하고 있으며, 반사를 사용하여 누락 된 메소드를 추가하기 위해 catch 블록에 작성된 코드가 있음을 깨달았습니다.
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 라이브러리 자체에 있습니다.
이 답변이 도움이되기를 바랍니다.
또 다른 더러운 해킹 솔루션은 반사입니다.
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를 사용한 PATCH 요청
If your server is using ASP.NET Core, you can simply add the following code to specify the HTTP method using the header X-HTTP-Method-Override
, as described in the accepted answer.
app.Use((context, next) => {
var headers = context.Request.Headers["X-HTTP-Method-Override"];
if(headers.Count == 1) {
context.Request.Method = headers.First();
}
return next();
});
Simply add this code in Startup.Configure
before your call to app.UseMvc()
.
In emulator of API 16 I received an exception: java.net.ProtocolException: Unknown method 'PATCH'; must be one of [OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE]
.
While an accepted answer works, I want to add one detail. In new APIs PATCH
works well, so in conjunction with https://github.com/OneDrive/onedrive-sdk-android/issues/16 you should write:
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);
}
I changed JELLY_BEAN_MR2
to KITKAT
after testing in API 16, 19, 21.
I got mine with Jersey client. The workaround was:
Client client = ClientBuilder.newClient();
client.property(HttpUrlConnectorProvider.SET_METHOD_WORKAROUND, true);
**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()));**
maven plugin
> <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>
use this really it would helps you
For anyone using Spring restTemplate looking for a detailed answer.
You will face the problem if you are using SimpleClientHttpRequestFactory as your restTemplate's ClientHttpRequestFactory.
From java.net.HttpURLConnection:
/* valid HTTP methods */
private static final String[] methods = {
"GET", "POST", "HEAD", "OPTIONS", "PUT", "DELETE", "TRACE"
};
As PATCH is not a supported operation, this line of code from the same class will execute:
throw new ProtocolException("Invalid HTTP method: " + method);
I ended up using the same as what @hirosht suggested in his answer.
참고URL : https://stackoverflow.com/questions/25163131/httpurlconnection-invalid-http-method-patch
'programing' 카테고리의 다른 글
HTML의 이미지 소스에서 이미지 회전 (0) | 2020.12.04 |
---|---|
IntelliJ IDEA에서 .gitignore에 파일 / 폴더를 추가하는 방법은 무엇입니까? (0) | 2020.12.04 |
Jquery : 잠자기 또는 지연하는 방법? (0) | 2020.12.03 |
"루트 요소가 없습니다."수정 방법 (0) | 2020.12.03 |
JSON 및 내 보내지 않은 필드 처리 (0) | 2020.12.03 |