차이 - java.net.URLConnection을 사용하여 HTTP 요청을 처리하고 처리하는 방법




urlconnection httpurlconnection 차이 (8)

최신 정보

새 HTTP 클라이언트는 Java 9와 함께 제공되었지만 jdk.incubator.httpclient 라는 인큐베이터 모듈의 일부로 제공됩니다. 인큐베이터 모듈은 비 최종 API를 개발자에게 제공하는 수단이며 API는 향후 릴리스에서 최종화 또는 제거로 진행됩니다.

Java 9에서는 다음과 같이 GET 요청을 보낼 수 있습니다.

// GET
HttpResponse response = HttpRequest
    .create(new URI("http://www.stackoverflow.com"))
    .headers("Foo", "foovalue", "Bar", "barvalue")
    .GET()
    .response();

그런 다음 반환 된 HttpResponse 검사 할 수 있습니다.

int statusCode = response.statusCode();
String responseBody = response.body(HttpResponse.asString());

이 새로운 HTTP 클라이언트는 java.httpclient jdk.incubator.httpclient 모듈을 사용하려면 module-info.java 파일에서이 종속성을 선언해야합니다.

module com.foo.bar {
    requires jdk.incubator.httpclient;
}

java.net.URLConnection 사용법은 여기에서 꽤 자주 묻습니다. Oracle 튜토리얼너무 간결합니다.

이 튜토리얼은 기본적으로 GET 요청을 실행하고 응답을 읽는 방법을 보여줍니다. 그것은 POST 요청을 수행하고, 요청 헤더를 설정하고, 응답 헤더를 읽고, 쿠키를 처리하고, HTML 양식을 제출하고, 파일을 업로드하는 등의 작업을 어디서 어떻게 사용하는지 설명하지 않습니다.

그렇다면 java.net.URLConnection 을 사용하여 "고급"HTTP 요청을 처리하고 처리하는 방법은 무엇입니까?


HTTP URL 조회로 갈 수있는 두 가지 옵션이 있습니다 : GET / POST

GET 요청 : -

HttpURLConnection.setFollowRedirects(true); // defaults to true

String url = "https://name_of_the_url";
URL request_url = new URL(url);
HttpURLConnection http_conn = (HttpURLConnection)request_url.openConnection();
http_conn.setConnectTimeout(100000);
http_conn.setReadTimeout(100000);
http_conn.setInstanceFollowRedirects(true);
System.out.println(String.valueOf(http_conn.getResponseCode()));

POST 요청 : -

HttpURLConnection.setFollowRedirects(true); // defaults to true

String url = "https://name_of_the_url"
URL request_url = new URL(url);
HttpURLConnection http_conn = (HttpURLConnection)request_url.openConnection();
http_conn.setConnectTimeout(100000);
http_conn.setReadTimeout(100000);
http_conn.setInstanceFollowRedirects(true);
http_conn.setDoOutput(true);
PrintWriter out = new PrintWriter(http_conn.getOutputStream());
if (urlparameter != null) {
   out.println(urlparameter);
}
out.close();
out = null;
System.out.println(String.valueOf(http_conn.getResponseCode()));

http를 사용하는 경우이 줄을 제거하십시오.

urlConnection.setDoOutput(true);

그래서이 질문과 다른 질문에 영감을 받아 최소한의 오픈 소스 basic-http-client 를 만들었습니다. 여기서는 대부분의 기술을 구현합니다.

google-http-java-client 또한 훌륭한 오픈 소스 리소스입니다.


나는 또한이 반응에 매우 고무되었다.

나는 종종 HTTP를 할 필요가있는 프로젝트에 참여하고 있으며 타사 종속성 (다른 것들을 가져 오는 등등)을 가져오고 싶지는 않습니다.

나는이 대화의 일부를 토대로 내 자신의 유틸리티를 작성하기 시작했다.

package org.boon.utils;


import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.Map;

import static org.boon.utils.IO.read;

public class HTTP {

그런 다음 묶음 또는 정적 메서드가 있습니다.

public static String get(
        final String url) {

    Exceptions.tryIt(() -> {
        URLConnection connection;
        connection = doGet(url, null, null, null);
        return extractResponseString(connection);
    });
    return null;
}

public static String getWithHeaders(
        final String url,
        final Map<String, ? extends Object> headers) {
    URLConnection connection;
    try {
        connection = doGet(url, headers, null, null);
        return extractResponseString(connection);
    } catch (Exception ex) {
        Exceptions.handle(ex);
        return null;
    }
}

public static String getWithContentType(
        final String url,
        final Map<String, ? extends Object> headers,
        String contentType) {
    URLConnection connection;
    try {
        connection = doGet(url, headers, contentType, null);
        return extractResponseString(connection);
    } catch (Exception ex) {
        Exceptions.handle(ex);
        return null;
    }
}
public static String getWithCharSet(
        final String url,
        final Map<String, ? extends Object> headers,
        String contentType,
        String charSet) {
    URLConnection connection;
    try {
        connection = doGet(url, headers, contentType, charSet);
        return extractResponseString(connection);
    } catch (Exception ex) {
        Exceptions.handle(ex);
        return null;
    }
}

그런 다음 게시 ...

public static String postBody(
        final String url,
        final String body) {
    URLConnection connection;
    try {
        connection = doPost(url, null, "text/plain", null, body);
        return extractResponseString(connection);
    } catch (Exception ex) {
        Exceptions.handle(ex);
        return null;
    }
}

public static String postBodyWithHeaders(
        final String url,
        final Map<String, ? extends Object> headers,
        final String body) {
    URLConnection connection;
    try {
        connection = doPost(url, headers, "text/plain", null, body);
        return extractResponseString(connection);
    } catch (Exception ex) {
        Exceptions.handle(ex);
        return null;
    }
}



public static String postBodyWithContentType(
        final String url,
        final Map<String, ? extends Object> headers,
        final String contentType,
        final String body) {

    URLConnection connection;
    try {
        connection = doPost(url, headers, contentType, null, body);


        return extractResponseString(connection);


    } catch (Exception ex) {
        Exceptions.handle(ex);
        return null;
    }


}


public static String postBodyWithCharset(
        final String url,
        final Map<String, ? extends Object> headers,
        final String contentType,
        final String charSet,
        final String body) {

    URLConnection connection;
    try {
        connection = doPost(url, headers, contentType, charSet, body);


        return extractResponseString(connection);


    } catch (Exception ex) {
        Exceptions.handle(ex);
        return null;
    }


}

private static URLConnection doPost(String url, Map<String, ? extends Object> headers,
                                    String contentType, String charset, String body
                                    ) throws IOException {
    URLConnection connection;/* Handle output. */
    connection = new URL(url).openConnection();
    connection.setDoOutput(true);
    manageContentTypeHeaders(contentType, charset, connection);

    manageHeaders(headers, connection);


    IO.write(connection.getOutputStream(), body, IO.CHARSET);
    return connection;
}

private static void manageHeaders(Map<String, ? extends Object> headers, URLConnection connection) {
    if (headers != null) {
        for (Map.Entry<String, ? extends Object> entry : headers.entrySet()) {
            connection.setRequestProperty(entry.getKey(), entry.getValue().toString());
        }
    }
}

private static void manageContentTypeHeaders(String contentType, String charset, URLConnection connection) {
    connection.setRequestProperty("Accept-Charset", charset == null ? IO.CHARSET : charset);
    if (contentType!=null && !contentType.isEmpty()) {
        connection.setRequestProperty("Content-Type", contentType);
    }
}

private static URLConnection doGet(String url, Map<String, ? extends Object> headers,
                                    String contentType, String charset) throws IOException {
    URLConnection connection;/* Handle output. */
    connection = new URL(url).openConnection();
    manageContentTypeHeaders(contentType, charset, connection);

    manageHeaders(headers, connection);

    return connection;
}

private static String extractResponseString(URLConnection connection) throws IOException {
/* Handle input. */
    HttpURLConnection http = (HttpURLConnection)connection;
    int status = http.getResponseCode();
    String charset = getCharset(connection.getHeaderField("Content-Type"));

    if (status==200) {
        return readResponseBody(http, charset);
    } else {
        return readErrorResponseBody(http, status, charset);
    }
}

private static String readErrorResponseBody(HttpURLConnection http, int status, String charset) {
    InputStream errorStream = http.getErrorStream();
    if ( errorStream!=null ) {
        String error = charset== null ? read( errorStream ) :
            read( errorStream, charset );
        throw new RuntimeException("STATUS CODE =" + status + "\n\n" + error);
    } else {
        throw new RuntimeException("STATUS CODE =" + status);
    }
}

private static String readResponseBody(HttpURLConnection http, String charset) throws IOException {
    if (charset != null) {
        return read(http.getInputStream(), charset);
    } else {
        return read(http.getInputStream());
    }
}

private static String getCharset(String contentType) {
    if (contentType==null)  {
        return null;
    }
    String charset = null;
    for (String param : contentType.replace(" ", "").split(";")) {
        if (param.startsWith("charset=")) {
            charset = param.split("=", 2)[1];
            break;
        }
    }
    charset = charset == null ?  IO.CHARSET : charset;

    return charset;
}

잘 생각해 보면 ....

테스트는 다음과 같습니다.

static class MyHandler implements HttpHandler {
    public void handle(HttpExchange t) throws IOException {

        InputStream requestBody = t.getRequestBody();
        String body = IO.read(requestBody);
        Headers requestHeaders = t.getRequestHeaders();
        body = body + "\n" + copy(requestHeaders).toString();
        t.sendResponseHeaders(200, body.length());
        OutputStream os = t.getResponseBody();
        os.write(body.getBytes());
        os.close();
    }
}


@Test
public void testHappy() throws Exception {

    HttpServer server = HttpServer.create(new InetSocketAddress(9212), 0);
    server.createContext("/test", new MyHandler());
    server.setExecutor(null); // creates a default executor
    server.start();

    Thread.sleep(10);


    Map<String,String> headers = map("foo", "bar", "fun", "sun");

    String response = HTTP.postBodyWithContentType("http://localhost:9212/test", headers, "text/plain", "hi mom");

    System.out.println(response);

    assertTrue(response.contains("hi mom"));
    assertTrue(response.contains("Fun=[sun], Foo=[bar]"));


    response = HTTP.postBodyWithCharset("http://localhost:9212/test", headers, "text/plain", "UTF-8", "hi mom");

    System.out.println(response);

    assertTrue(response.contains("hi mom"));
    assertTrue(response.contains("Fun=[sun], Foo=[bar]"));

    response = HTTP.postBodyWithHeaders("http://localhost:9212/test", headers, "hi mom");

    System.out.println(response);

    assertTrue(response.contains("hi mom"));
    assertTrue(response.contains("Fun=[sun], Foo=[bar]"));


    response = HTTP.get("http://localhost:9212/test");

    System.out.println(response);


    response = HTTP.getWithHeaders("http://localhost:9212/test", headers);

    System.out.println(response);

    assertTrue(response.contains("Fun=[sun], Foo=[bar]"));



    response = HTTP.getWithContentType("http://localhost:9212/test", headers, "text/plain");

    System.out.println(response);

    assertTrue(response.contains("Fun=[sun], Foo=[bar]"));



    response = HTTP.getWithCharSet("http://localhost:9212/test", headers, "text/plain", "UTF-8");

    System.out.println(response);

    assertTrue(response.contains("Fun=[sun], Foo=[bar]"));

    Thread.sleep(10);

    server.stop(0);


}

@Test
public void testPostBody() throws Exception {

    HttpServer server = HttpServer.create(new InetSocketAddress(9220), 0);
    server.createContext("/test", new MyHandler());
    server.setExecutor(null); // creates a default executor
    server.start();

    Thread.sleep(10);


    Map<String,String> headers = map("foo", "bar", "fun", "sun");

    String response = HTTP.postBody("http://localhost:9220/test", "hi mom");

    assertTrue(response.contains("hi mom"));


    Thread.sleep(10);

    server.stop(0);


}

@Test(expected = RuntimeException.class)
public void testSad() throws Exception {

    HttpServer server = HttpServer.create(new InetSocketAddress(9213), 0);
    server.createContext("/test", new MyHandler());
    server.setExecutor(null); // creates a default executor
    server.start();

    Thread.sleep(10);


    Map<String,String> headers = map("foo", "bar", "fun", "sun");

    String response = HTTP.postBodyWithContentType("http://localhost:9213/foo", headers, "text/plain", "hi mom");

    System.out.println(response);

    assertTrue(response.contains("hi mom"));
    assertTrue(response.contains("Fun=[sun], Foo=[bar]"));

    Thread.sleep(10);

    server.stop(0);


}

여기서 나머지를 찾을 수 있습니다 :

https://github.com/RichardHightower/boon

내 목표는 사람들이 좀 더 쉬운 방법으로하고 싶은 일반적인 것들을 제공하는 것입니다 ....


처음에는 HttpClient 호의적 인이 article 로 잘못 HttpClient .

나중에 나는 HttpURLConnection 이이 article 에서 머무를 것이라는 것을 깨달았다.

Google 블로그 당 :

Apache HTTP 클라이언트는 Eclair와 Froyo에 대한 버그가 적습니다. 이 릴리스에 가장 적합한 선택입니다. Gingerbread의 경우 HttpURLConnection이 최선의 선택입니다. 간단한 API와 작은 크기로 인해 Android에 매우 적합합니다.

투명한 압축 및 응답 캐싱은 네트워크 사용을 줄이고 속도를 개선하며 배터리를 절약합니다. 새로운 응용 프로그램은 HttpURLConnection을 사용해야합니다. 그것은 우리가 앞으로 나아갈 에너지를 쓰는 곳입니다.

rapidvaluesolutions.com/tech_blog/…HttpURLConnection 질문에 대한 다른 스택을 읽은 후 HttpURLConnection 이 더 오랜 기간 동안 머물 것이라고 확신합니다.

HttpURLConnections 선호하는 SE 질문 중 일부는 다음과 같습니다.

안드로이드에서 UrlEncodedFormEntity를 사용하지 않고 URL 인코딩 된 폼 데이터로 POST 요청을하십시오.

HttpPost는 Android 프로젝트가 아닌 Java 프로젝트에서 작동합니다.


kevinsawicki/http-request 의 코드를 살펴 보시기 바랍니다. 기본적으로 HttpUrlConnection 맨 위에 래퍼가 있습니다. 요청을 지금 바로 만들고 싶거나 소스를 살펴볼 수있는 경우 훨씬 더 간단한 API를 제공합니다. 연결이 어떻게 처리되는지 살펴 보는 것은 너무 큽니다.

예 : 콘텐츠 유형 application/json 및 일부 쿼리 매개 변수를 사용하여 GET 요청을 작성합니다.

// GET http://google.com?q=baseball%20gloves&size=100
String response = HttpRequest.get("http://google.com", true, "q", "baseball gloves", "size", 100)
        .accept("application/json")
        .body();
System.out.println("Response was: " + response);

먼저 면책 조항 : 게시 된 코드 스 니펫은 모두 기본적인 예제입니다. 간단한 IOExceptionNullPointerException , ArrayIndexOutOfBoundsException 과 같은 RuntimeException 을 처리 할 필요가 있으며 스스로를 조율한다.

준비중 인

먼저 URL과 charset을 최소한 알아야합니다. 매개 변수는 선택적이며 기능 요구 사항에 따라 다릅니다.

String url = "http://example.com";
String charset = "UTF-8";  // Or in Java 7 and later, use the constant: java.nio.charset.StandardCharsets.UTF_8.name()
String param1 = "value1";
String param2 = "value2";
// ...

String query = String.format("param1=%s&param2=%s", 
     URLEncoder.encode(param1, charset), 
     URLEncoder.encode(param2, charset));

쿼리 매개 변수는 name=value 형식이어야하며 & 로 연결해야합니다. 일반적으로 URLEncoder#encode() 사용하여 쿼리 매개 변수를 지정된 charset으로 URL-encode 합니다.

String#format() 은 편의를 위해서입니다. 나는 String Concatenation 연산자 + 두 번 이상 필요로 할 때 선호한다.

쿼리 매개 변수 (선택 사항)와 함께 HTTP GET 요청 실행

그것은 사소한 일입니다. 기본 요청 방법입니다.

URLConnection connection = new URL(url + "?" + query).openConnection();
connection.setRequestProperty("Accept-Charset", charset);
InputStream response = connection.getInputStream();
// ...

모든 쿼리 문자열은 ? 사용하여 URL에 연결되어야합니다 ? . Accept-Charset 헤더는 매개 변수가 들어있는 인코딩을 서버에 Accept-Charset 수 있습니다. 쿼리 문자열을 보내지 않으면 Accept-Charset 헤더를 멀리 둘 수 있습니다. 머리글을 설정할 필요가없는 경우 URL#openStream() 바로 가기 방법을 사용할 수도 있습니다.

InputStream response = new URL(url).openStream();
// ...

어느 쪽이든, 다른 쪽이 HttpServlet 인 경우 doGet() 메서드가 호출되고 매개 변수는 HttpServletRequest#getParameter() 사용할 수 있습니다.

테스트 목적으로 아래와 같이 응답 본문을 stdout에 인쇄 할 수 있습니다.

try (Scanner scanner = new Scanner(response)) {
    String responseBody = scanner.useDelimiter("\\A").next();
    System.out.println(responseBody);
}

쿼리 매개 변수로 HTTP POST 요청 실행

URLConnection#setDoOutput()true 설정하면 암시 적으로 요청 메소드가 POST로 설정됩니다. 웹 폼처럼 표준 HTTP POST는 application/x-www-form-urlencoded 유형이며 쿼리 문자열은 요청 본문에 기록됩니다.

URLConnection connection = new URL(url).openConnection();
connection.setDoOutput(true); // Triggers POST.
connection.setRequestProperty("Accept-Charset", charset);
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded;charset=" + charset);

try (OutputStream output = connection.getOutputStream()) {
    output.write(query.getBytes(charset));
}

InputStream response = connection.getInputStream();
// ...

참고 : 프로그래밍 방식으로 HTML 양식을 제출할 때마다 <input type="hidden"> 요소의 name=value 쌍을 쿼리 문자열로 가져 오는 것을 잊지 말고 물론 name=value 쌍도 포함시켜야합니다. 프로그래밍 방식으로 "누르기"를 원할 <input type="submit"> 요소입니다 (버튼이 눌려 졌는지 여부를 구별하기 위해 일반적으로 서버 측에서 사용 되었기 때문에, 어느 것이 눌려 졌는지 구분할 수 있습니다).

또한 얻은 java.net.URLConnectionHttpURLConnection 캐스팅하고 대신 HttpURLConnection#setRequestMethod() 를 사용할 수 있습니다. 그러나 출력을 위해 연결을 사용하려는 경우 URLConnection#setDoOutput()true 로 설정해야 true .

HttpURLConnection httpConnection = (HttpURLConnection) new URL(url).openConnection();
httpConnection.setRequestMethod("POST");
// ...

어느 쪽이든, 다른 쪽이 HttpServlet 인 경우, doPost() 메소드가 호출되고 HttpServletRequest#getParameter() 가 매개 변수를 사용할 수 있습니다.

실제로 HTTP 요청을 실행 중입니다.

URLConnection#connect() 하여 HTTP 요청을 명시 적으로 시작할 수 있지만 URLConnection#getInputStream() 사용하여 응답 본문과 같은 HTTP 응답에 대한 정보를 얻으려는 경우 요청이 자동으로 요청에 따라 실행됩니다. 위의 예제는 정확히 그렇게하므로 connect() 호출은 실제로 불필요합니다.

HTTP 응답 정보 수집

  1. HTTP 응답 상태 :

    여기에 HttpURLConnection 이 필요합니다. 필요한 경우 먼저 던지십시오.

    int status = httpConnection.getResponseCode();
    
  2. HTTP 응답 헤더 :

    for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) {
        System.out.println(header.getKey() + "=" + header.getValue());
    }
    
  3. HTTP 응답 인코딩 :

    Content-Typecharset 매개 변수가 포함되어 있으면 응답 본문이 텍스트 기반 일 수 있으므로 서버 측 지정 문자 인코딩을 사용하여 응답 본문을 처리하려고합니다.

    String contentType = connection.getHeaderField("Content-Type");
    String charset = null;
    
    for (String param : contentType.replace(" ", "").split(";")) {
        if (param.startsWith("charset=")) {
            charset = param.split("=", 2)[1];
            break;
        }
    }
    
    if (charset != null) {
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(response, charset))) {
            for (String line; (line = reader.readLine()) != null;) {
                // ... System.out.println(line) ?
            }
        }
    } else {
        // It's likely binary content, use InputStream/OutputStream.
    }
    

세션 유지 보수

서버 측 세션은 대개 쿠키에 의해 백업됩니다. 일부 웹 양식을 사용하려면 세션에 로그인하거나 추적하는 것이 필요합니다. CookieHandler API를 사용하여 쿠키를 유지 관리 할 수 ​​있습니다. 모든 HTTP 요청을 보내기 전에 ACCEPT_ALLCookiePolicy 가있는 CookieManager 를 준비해야합니다.

// First set the default cookie manager.
CookieHandler.setDefault(new CookieManager(null, CookiePolicy.ACCEPT_ALL));

// All the following subsequent URLConnections will use the same cookie manager.
URLConnection connection = new URL(url).openConnection();
// ...

connection = new URL(url).openConnection();
// ...

connection = new URL(url).openConnection();
// ...

이는 모든 상황에서 항상 올바르게 작동하는 것은 아니라는 점에 유의하십시오. 실패 할 경우 쿠키 헤더를 수동으로 수집하고 설정하는 것이 가장 좋습니다. 기본적으로 로그인 또는 첫 번째 GET 요청의 응답에서 모든 Set-Cookie 헤더를 가져온 다음 후속 요청을 통해이를 전달해야합니다.

// Gather all cookies on the first request.
URLConnection connection = new URL(url).openConnection();
List<String> cookies = connection.getHeaderFields().get("Set-Cookie");
// ...

// Then use the same cookies on all subsequent requests.
connection = new URL(url).openConnection();
for (String cookie : cookies) {
    connection.addRequestProperty("Cookie", cookie.split(";", 2)[0]);
}
// ...

split(";", 2)[0]expires , path 등과 같이 서버 측과 관련이없는 쿠키 속성을 제거하기위한 것입니다. 또는 cookie.substring(0, cookie.indexOf(';')) 대신에 split() 합니다.

스트리밍 모드

기본적으로 HttpURLConnectionconnection.setRequestProperty("Content-Length", contentLength); 사용하여 고정 된 콘텐츠 길이를 직접 설정했는지 여부에 관계없이 실제로 요청하기 전에 전체 요청 본문을 버퍼링합니다 connection.setRequestProperty("Content-Length", contentLength); . 이로 인해 큰 POST 요청 (예 : 파일 업로드)을 동시에 보낼 때마다 OutOfMemoryException 이 발생할 수 있습니다. 이것을 피하려면 HttpURLConnection#setFixedLengthStreamingMode() 를 설정하고 싶습니다.

httpConnection.setFixedLengthStreamingMode(contentLength);

그러나 콘텐츠 길이가 미리 알려지지 않은 경우 HttpURLConnection#setChunkedStreamingMode() 적절하게 설정하여 청크 분할 모드를 사용할 수 있습니다. 이렇게하면 HTTP Transfer-Encoding 헤더가 chunked 되어 요청 본문이 청크로 전송됩니다. 아래 예제는 몸체를 1KB 청크로 보냅니다.

httpConnection.setChunkedStreamingMode(1024);

사용자 에이전트

요청이 예기치 않은 응답을 반환하고 실제 웹 브라우저에서는 정상적으로 작동합니다 . 서버 측은 아마 User-Agent 요청 헤더에 기반한 요청을 차단하고 있습니다. URLConnection 은 기본적으로이를 Java/1.6.0_19 로 설정합니다. 마지막 부분은 분명히 JRE 버전입니다. 이를 다음과 같이 재정의 할 수 있습니다.

connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36"); // Do as if you're using Chrome 41 on Windows 7.

최근 브라우저 의 User-Agent 문자열을 사용하십시오.

오류 처리

HTTP 응답 코드가 4nn (클라이언트 오류) 또는 5nn (서버 오류) 인 경우 HttpURLConnection#getErrorStream() 을 읽으면 서버가 유용한 오류 정보를 보냈는지 확인할 수 있습니다.

InputStream error = ((HttpURLConnection) connection).getErrorStream();

HTTP 응답 코드가 -1이면 연결 및 응답 처리에 문제가 발생합니다. HttpURLConnection 구현은 오래된 JRE에서 연결을 유지하면서 다소 버그가 있습니다. http.keepAlive 시스템 속성을 false 로 설정하여 해제 할 수 있습니다. 다음과 같이 프로그램 시작 부분에서 프로그래밍 방식으로이 작업을 수행 할 수 있습니다.

System.setProperty("http.keepAlive", "false");

파일 업로드 중

일반적으로 혼합 된 POST 내용 (바이너리 및 문자 데이터)에 multipart/form-data 인코딩을 사용합니다. 인코딩은 RFC2388 자세히 설명되어 RFC2388 .

String param = "value";
File textFile = new File("/path/to/file.txt");
File binaryFile = new File("/path/to/file.bin");
String boundary = Long.toHexString(System.currentTimeMillis()); // Just generate some unique random value.
String CRLF = "\r\n"; // Line separator required by multipart/form-data.
URLConnection connection = new URL(url).openConnection();
connection.setDoOutput(true);
connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);

try (
    OutputStream output = connection.getOutputStream();
    PrintWriter writer = new PrintWriter(new OutputStreamWriter(output, charset), true);
) {
    // Send normal param.
    writer.append("--" + boundary).append(CRLF);
    writer.append("Content-Disposition: form-data; name=\"param\"").append(CRLF);
    writer.append("Content-Type: text/plain; charset=" + charset).append(CRLF);
    writer.append(CRLF).append(param).append(CRLF).flush();

    // Send text file.
    writer.append("--" + boundary).append(CRLF);
    writer.append("Content-Disposition: form-data; name=\"textFile\"; filename=\"" + textFile.getName() + "\"").append(CRLF);
    writer.append("Content-Type: text/plain; charset=" + charset).append(CRLF); // Text file itself must be saved in this charset!
    writer.append(CRLF).flush();
    Files.copy(textFile.toPath(), output);
    output.flush(); // Important before continuing with writer!
    writer.append(CRLF).flush(); // CRLF is important! It indicates end of boundary.

    // Send binary file.
    writer.append("--" + boundary).append(CRLF);
    writer.append("Content-Disposition: form-data; name=\"binaryFile\"; filename=\"" + binaryFile.getName() + "\"").append(CRLF);
    writer.append("Content-Type: " + URLConnection.guessContentTypeFromName(binaryFile.getName())).append(CRLF);
    writer.append("Content-Transfer-Encoding: binary").append(CRLF);
    writer.append(CRLF).flush();
    Files.copy(binaryFile.toPath(), output);
    output.flush(); // Important before continuing with writer!
    writer.append(CRLF).flush(); // CRLF is important! It indicates end of boundary.

    // End of multipart/form-data.
    writer.append("--" + boundary + "--").append(CRLF).flush();
}

다른 쪽이 HttpServlet 인 경우 doPost() 메서드가 호출되고 HttpServletRequest#getPart() 에서 부품을 사용할 수 있습니다 (따라서 getParameter() 등은 아닙니다 !). 그러나 getPart() 메소드는 상대적으로 새로운 것으로 getPart() Servlet 3.0 (Glassfish 3, Tomcat 7 등)에서 소개되었습니다. Servlet 3.0 이전에는 Apache Commons FileUpload 를 사용하여 multipart/form-data 요청을 구문 분석하는 것이 가장 좋습니다. 또한 FileUpload와 Servelt 3.0 접근법에 대한 예제를 보려면 이 대답 을 참조하십시오.

신뢰할 수 없거나 잘못 구성된 HTTPS 사이트 다루기

때로는 웹 스크래퍼를 쓰고 있기 때문에 HTTPS URL에 연결해야 할 수도 있습니다. 이 경우, SSL 인증서를 최신 상태로 유지하지 않는 일부 HTTPS 사이트에서 javax.net.ssl.SSLException: Not trusted server certificate 또는 java.security.cert.CertificateException: No subject alternative DNS name matching [hostname] found 또는 javax.net.ssl.SSLProtocolException: handshake alert: unrecognized_name 일부 잘못된 HTTPS 사이트에서 javax.net.ssl.SSLProtocolException: handshake alert: unrecognized_name .

웹 스크래퍼 클래스의 다음 번 일회성 static 이니셜 라이저는 HTTPS 사이트에 대해 HttpsURLConnection 좀 더 관대하게 만들어 더 이상 예외를 throw하지 않아야합니다.

static {
    TrustManager[] trustAllCertificates = new TrustManager[] {
        new X509TrustManager() {
            @Override
            public X509Certificate[] getAcceptedIssuers() {
                return null; // Not relevant.
            }
            @Override
            public void checkClientTrusted(X509Certificate[] certs, String authType) {
                // Do nothing. Just allow them all.
            }
            @Override
            public void checkServerTrusted(X509Certificate[] certs, String authType) {
                // Do nothing. Just allow them all.
            }
        }
    };

    HostnameVerifier trustAllHostnames = new HostnameVerifier() {
        @Override
        public boolean verify(String hostname, SSLSession session) {
            return true; // Just allow them all.
        }
    };

    try {
        System.setProperty("jsse.enableSNIExtension", "false");
        SSLContext sc = SSLContext.getInstance("SSL");
        sc.init(null, trustAllCertificates, new SecureRandom());
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
        HttpsURLConnection.setDefaultHostnameVerifier(trustAllHostnames);
    }
    catch (GeneralSecurityException e) {
        throw new ExceptionInInitializerError(e);
    }
}

마지막 말

Apache HttpComponents HttpClient훨씬 더 편리합니다. :)

HTML 구문 분석 및 추출

HTML에서 파싱하고 데이터를 추출하는 것이 전부라면 Jsoup 와 같은 HTML 파서를 사용하는 것이 좋습니다.





urlconnection