Làm cách nào để sử dụng java.net.URLC Connectection để kích hoạt và xử lý các yêu cầu HTTP?


1948

Sử dụng java.net.URLConnection được hỏi về khá thường xuyên ở đây, và Oracle hướng dẫnquá ngắn gọn về nó.

Hướng dẫn đó về cơ bản chỉ cho thấy cách kích hoạt yêu cầu GET và đọc phản hồi. Nó không giải thích bất cứ nơi nào làm thế nào để sử dụng nó cho những người khác thực hiện yêu cầu POST, đặt tiêu đề yêu cầu, đọc tiêu đề phản hồi, xử lý cookie, gửi biểu mẫu HTML, tải lên tệp, v.v.

Vậy, làm cách nào tôi có thể sử dụng java.net.URLConnectionđể kích hoạt và xử lý các yêu cầu HTTP "nâng cao"?

Câu trả lời:


2711

Đầu tiên từ chối trách nhiệm trước: đoạn mã được đăng là tất cả các ví dụ cơ bản. Bạn sẽ cần phải xử lý những thứ tầm thường IOExceptionRuntimeExceptionthích NullPointerException, ArrayIndexOutOfBoundsExceptionvà tự phối ngẫu.


Chuẩn bị

Trước tiên chúng ta cần biết ít nhất là URL và bộ ký tự. Các tham số là tùy chọn và phụ thuộc vào các yêu cầu chức năng.

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

Các tham số truy vấn phải ở name=valueđịnh dạng và được nối bởi &. Thông thường bạn cũng sẽ mã hóa URL các tham số truy vấn bằng bộ ký tự được chỉ định bằng cách sử dụng URLEncoder#encode().

Chỉ String#format()là cho thuận tiện. Tôi thích nó khi tôi cần toán tử nối chuỗi +hơn hai lần.


Thực hiện yêu cầu HTTP GET với các tham số truy vấn (tùy chọn)

Đó là một nhiệm vụ tầm thường. Đây là phương thức yêu cầu mặc định.

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

Bất kỳ chuỗi truy vấn nào cũng cần được nối với URL bằng cách sử dụng ?. Các Accept-Charsettiêu đề có thể gợi ý các máy chủ gì mã hóa các thông số. Nếu bạn không gửi bất kỳ chuỗi truy vấn, sau đó bạn có thể để Accept-Charsettiêu đề ra. Nếu bạn không cần đặt bất kỳ tiêu đề nào, thì bạn thậm chí có thể sử dụng URL#openStream()phương pháp phím tắt.

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

Dù bằng cách nào, nếu phía bên kia là a HttpServlet, thì doGet()phương thức của nó sẽ được gọi và các tham số sẽ có sẵn bởi HttpServletRequest#getParameter().

Đối với mục đích thử nghiệm, bạn có thể in phần thân phản hồi ra thiết bị xuất chuẩn như dưới đây:

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

Bắn một yêu cầu POST HTTP với các tham số truy vấn

Đặt URLConnection#setDoOutput()thành truengầm định đặt phương thức yêu cầu thành POST. POST HTTP tiêu chuẩn như các biểu mẫu web thực hiện là loại application/x-www-form-urlencodedtrong đó chuỗi truy vấn được ghi vào phần thân yêu cầu.

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

Lưu ý: bất cứ khi nào bạn muốn gửi biểu mẫu HTML theo chương trình, đừng quên đưa các name=valuecặp <input type="hidden">phần tử bất kỳ vào chuỗi truy vấn và tất nhiên cũng là name=valuecặp của<input type="submit"> phần tử mà bạn muốn "nhấn" theo chương trình (vì thường được sử dụng ở phía máy chủ để phân biệt nếu nhấn nút và nếu có thì nút nào).

Bạn cũng có thể cast được URLConnectionđến HttpURLConnectionvà sử dụng nó HttpURLConnection#setRequestMethod()để thay thế. Nhưng nếu bạn đang cố gắng sử dụng kết nối cho đầu ra, bạn vẫn cần phải đặt URLConnection#setDoOutput()thành true.

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

Dù bằng cách nào, nếu phía bên kia là a HttpServlet, thì doPost()phương thức của nó sẽ được gọi và các tham số sẽ có sẵn bởi HttpServletRequest#getParameter().


Thực tế bắn yêu cầu HTTP

Bạn có thể thực hiện yêu cầu HTTP một cách rõ ràng URLConnection#connect(), nhưng yêu cầu sẽ tự động được thực hiện theo yêu cầu khi bạn muốn nhận bất kỳ thông tin nào về phản hồi HTTP, chẳng hạn như cơ quan phản hồi sử dụng URLConnection#getInputStream(), v.v. Các ví dụ trên thực hiện chính xác điều đó, vì vậy connect()cuộc gọi thực tế là không cần thiết.


Thu thập thông tin phản hồi HTTP

  1. Trạng thái phản hồi HTTP :

    Bạn cần một HttpURLConnectionở đây. Đúc nó trước nếu cần thiết.

    int status = httpConnection.getResponseCode();
  2. Tiêu đề phản hồi HTTP :

    for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) {
        System.out.println(header.getKey() + "=" + header.getValue());
    }
  3. Mã hóa phản hồi HTTP :

    Khi Content-Typechứa charsettham số, thì phần thân phản hồi có khả năng dựa trên văn bản và chúng tôi muốn xử lý phần thân phản hồi bằng mã hóa ký tự được chỉ định ở phía máy chủ.

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

Duy trì phiên

Phiên phía máy chủ thường được hỗ trợ bởi một cookie. Một số biểu mẫu web yêu cầu bạn đăng nhập và / hoặc được theo dõi bởi một phiên. Bạn có thể sử dụng CookieHandlerAPI để duy trì cookie. Bạn cần phải chuẩn bị một CookieManagervới một CookiePolicysố ACCEPT_ALLtrước khi gửi tất cả các yêu cầu HTTP.

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

Lưu ý rằng điều này được biết là không phải lúc nào cũng hoạt động đúng trong mọi trường hợp. Nếu nó không thành công với bạn, thì tốt nhất là thu thập thủ công và đặt các tiêu đề cookie. Về cơ bản bạn cần phải lấy tất cảSet-Cookie tiêu đề từ phản hồi của đăng nhập hoặc GETyêu cầu đầu tiên và sau đó chuyển thông qua các yêu cầu tiếp theo.

// 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]để loại bỏ các thuộc tính cookie không liên quan đến phía máy chủ như expires,path vv Ngoài ra, bạn cũng có thể sử dụng cookie.substring(0, cookie.indexOf(';'))thay vì split().


Chế độ truyền phát

Ý HttpURLConnectionchí theo mặc định sẽ đệm toàn bộ cơ thể yêu cầu trước khi thực sự gửi nó, bất kể bạn có tự đặt thời lượng nội dung cố định hay không connection.setRequestProperty("Content-Length", contentLength);. Điều này có thể gây ra OutOfMemoryExceptionbất cứ khi nào bạn gửi đồng thời các yêu cầu POST lớn (ví dụ: tải lên tệp). Để tránh điều này, bạn muốn đặt HttpURLConnection#setFixedLengthStreamingMode().

httpConnection.setFixedLengthStreamingMode(contentLength);

Nhưng nếu độ dài nội dung thực sự không được biết trước, thì bạn có thể sử dụng chế độ phát trực tuyến chunk bằng cách đặt HttpURLConnection#setChunkedStreamingMode()tương ứng. Điều này sẽ đặt Transfer-Encodingtiêu đề HTTP chunkedsẽ buộc phần thân yêu cầu được gửi theo khối. Ví dụ dưới đây sẽ gửi phần thân trong khối 1KB.

httpConnection.setChunkedStreamingMode(1024);

Đại lý người dùng

Nó có thể xảy ra rằng một yêu cầu trả về một phản hồi không mong muốn, trong khi nó hoạt động tốt với một trình duyệt web thực sự . Phía máy chủ có thể đang chặn các yêu cầu dựa trên User-Agenttiêu đề yêu cầu. Ý URLConnectionchí theo mặc định sẽ đặt nó Java/1.6.0_19ở nơi phần cuối rõ ràng là phiên bản JRE. Bạn có thể ghi đè lên điều này như sau:

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.

Sử dụng chuỗi User-Agent từ một trình duyệt gần đây .


Xử lý lỗi

Nếu mã phản hồi HTTP là 4nn(Lỗi máy khách) hoặc 5nn(Lỗi máy chủ), thì bạn có thể muốn đọc HttpURLConnection#getErrorStream()để xem máy chủ có gửi bất kỳ thông tin lỗi hữu ích nào không.

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

Nếu mã phản hồi HTTP là -1, thì có vấn đề với việc xử lý kết nối và phản hồi. Việc HttpURLConnectiontriển khai là trong các JRE cũ có phần lỗi với việc giữ kết nối còn sống. Bạn có thể muốn tắt nó bằng cách đặt thuộc tính http.keepAlivehệ thống thành false. Bạn có thể thực hiện việc này theo chương trình khi bắt đầu ứng dụng của mình bằng cách:

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

Đang tải lên tập tin

Bạn thường sử dụng multipart/form-datamã hóa cho nội dung POST hỗn hợp (dữ liệu nhị phân và ký tự). Mã hóa chi tiết hơn được mô tả trong 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();
}

Nếu phía bên kia là một HttpServlet, thì doPost()phương thức của nó sẽ được gọi và các phần sẽ có sẵn bằng cách HttpServletRequest#getPart()(lưu ý, do đó, không phải getParameter() như vậy!). Các getPart()phương pháp được tuy nhiên tương đối mới, nó được giới thiệu trong Servlet 3.0 (Glassfish 3, Tomcat 7, vv). Trước Servlet 3.0, lựa chọn tốt nhất của bạn là sử dụng Apache Commons FileUpload để phân tích một multipart/form-datayêu cầu. Cũng xem câu trả lời này để biết ví dụ về cả hai cách tiếp cận FileUpload và Servelt 3.0.


Xử lý các trang web HTTPS không đáng tin cậy hoặc bị định cấu hình sai

Đôi khi bạn cần kết nối URL HTTPS, có lẽ vì bạn đang viết một trình quét web. Trong trường hợp đó, bạn có thể phải đối mặt với một javax.net.ssl.SSLException: Not trusted server certificatesố trang web HTTPS không cập nhật chứng chỉ SSL hoặc một java.security.cert.CertificateException: No subject alternative DNS name matching [hostname] foundhoặc javax.net.ssl.SSLProtocolException: handshake alert: unrecognized_nametrên một số trang web HTTPS bị định cấu hình sai.

Trình statickhởi tạo chạy một lần sau đây trong lớp trình quét web của bạn sẽ giúp dễ HttpsURLConnectiondàng hơn đối với các trang web HTTPS đó và do đó không ném các ngoại lệ đó nữa.

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

Những từ cuối

Các Apache HttpClient HttpComponentsnhiều thuận tiện hơn trong tất cả điều này :)


Phân tích cú pháp và trích xuất HTML

Nếu tất cả những gì bạn muốn là phân tích cú pháp và trích xuất dữ liệu từ HTML, thì tốt hơn nên sử dụng trình phân tích cú pháp HTML như Jsoup


119
Bạn nên đặt liên kết apache trước, để mọi người tìm kiếm giải pháp tìm thấy nó nhanh hơn;)
ZeissS

40
@ivanceras: Nếu bạn không thể đun sôi nó dựa trên thông tin trong câu trả lời này, thì vui lòng nhấn Ask Questionnút ở trên cùng bên phải.
BalusC

3
@Brais: Vui lòng đọc thông số kỹ thuật. Phần --không phải là một phần của ranh giới chính nó. Nó chỉ là một chuỗi phân cách. Tôi đã khôi phục chỉnh sửa không hợp lệ của bạn.
BalusC

7
@BalusC cảm ơn rất nhiều vì một hướng dẫn hoàn hảo như vậy. Vui lòng bao gồm một tiêu đề như "Đóng luồng / kết nối". Tôi thực sự bối rối về việc khi nào và những luồng / kết nối nào sẽ đóng.

10
Điều đáng buồn là trên Android hiện không nên sử dụng Apache HttpClientHttpURLConnectionrất tàn nhẫn. android-developers.blogspot.in/2011/09/...
Yati sagade

91

Khi làm việc với HTTP, hầu như luôn hữu ích hơn khi tham khảo HttpURLConnectionthay vì lớp cơ sở URLConnection(vì đây URLConnectionlà lớp trừu tượng khi bạn yêu cầu URLConnection.openConnection()URL HTTP mà bạn sẽ nhận lại bằng cách nào).

Sau đó, bạn có thể thay vì dựa vào URLConnection#setDoOutput(true)để đặt ngầm định phương thức yêu cầu thành POST thay vì làmhttpURLConnection.setRequestMethod("POST") đó, một số có thể thấy tự nhiên hơn (và cũng cho phép bạn chỉ định các phương thức yêu cầu khác như PUT , DELETE , ...).

Nó cũng cung cấp các hằng số HTTP hữu ích để bạn có thể làm:

int responseCode = httpURLConnection.getResponseCode();

if (responseCode == HttpURLConnection.HTTP_OK) {

1
setDoOutPut true là vấn đề của tôi trong đó đặt GET của tôi thành POST. Cảm ơn
Patrick Kafka

22
Nếu bạn đang cố ghi dữ liệu vào đầu ra, bạn vẫn phải đặt setDoOutput()thành truenếu không sẽ có ngoại lệ (ngay cả khi bạn setRequestMethod("POST")). Để rõ ràng: cài đặt URLConnection#setDoOutput(true)thành truengầm định đặt phương thức yêu cầu thành POST, nhưng cài đặt httpURLConnection.setRequestMethod("POST")thành POST không hoàn toàn được đặt setDoOutput()thành true.
Tony Chan

54

Lấy cảm hứng từ câu hỏi này và các câu hỏi khác về SO, tôi đã tạo ra một nguồn mở tối thiểu máy khách http-client cơ bản , thể hiện hầu hết các kỹ thuật được tìm thấy ở đây.

google-http-java-client cũng là một tài nguyên nguồn mở tuyệt vời.


Tôi chỉ nghĩ giống nhau. Nhưng thật tuyệt khi có một thư viện Java đơn giản / barebones chỉ sử dụng mã URLConnection như được trình bày ở đây, đóng gói mã thành các phương thức đơn giản hơn để thực hiện HTTP GET, POST, v.v. Thư viện sau đó có thể được biên dịch & đóng gói dưới dạng JAR và được nhập / sử dụng trong mã Java hoặc tệp lớp nguồn có thể được bao gồm trong dự án Java nếu không muốn các JAR bên ngoài. Điều này có thể được thực hiện với các thư viện khác như Apache, v.v. nhưng khó hơn so với thư viện lớp 1 tệp đơn giản sử dụng URLConnection.
David

quickvaluesolutions.com/tech_blog/ ủng hộ kết nối httpURLC trên httpClient
Ravindra babu

24

Tôi đề nghị bạn hãy xem mã trên kevinsawicki / http-request , về cơ bản nó là một trình bao bọc ở trên cùng củaHttpUrlConnection nó cung cấp API đơn giản hơn nhiều trong trường hợp bạn chỉ muốn thực hiện các yêu cầu ngay bây giờ hoặc bạn có thể xem các nguồn ( nó không quá lớn) để xem cách các kết nối được xử lý.

Ví dụ: Thực hiện một GETyêu cầu với loại nội dung application/jsonvà một số tham số truy vấn:

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

24

Có 2 tùy chọn bạn có thể thực hiện với Số lần truy cập URL HTTP: GET / POST

NHẬN Yêu cầu: -

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

Yêu cầu 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()));

3
Làm thế nào bạn có thể xem phản hồi JSON thực tế?
Sora

21

Tôi cũng rất cảm hứng với phản ứng này.

Tôi thường tham gia vào các dự án mà tôi cần thực hiện một số HTTP và tôi có thể không muốn mang lại nhiều sự phụ thuộc của bên thứ 3 (điều này mang lại cho những người khác, v.v., v.v.)

Tôi bắt đầu viết các tiện ích của riêng mình dựa trên một số cuộc trò chuyện này (không phải bất kỳ nơi nào được thực hiện):

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 {

Sau đó, chỉ có một bó hoặc phương thức tĩnh.

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

Sau đó đăng ...

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

Chà, bạn hiểu ý rồi đó....

Dưới đây là các bài kiểm tra:

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


}

Bạn có thể tìm thấy phần còn lại ở đây:

https://github.com/RichardHightower/boon

Mục tiêu của tôi là cung cấp những điều phổ biến mà người ta muốn làm theo cách dễ dàng hơn một chút sau đó ....


2
Thật kỳ lạ khi trong doPostphương thức có một charsetparam, được sử dụng để đặt tiêu đề yêu cầu, nhưng sau đó dữ liệu được ghi bằng một bộ ký tự mã hóa cứng IO.CHARSET. Một lỗi?
Vit Khudenko

21

Cập nhật

Máy khách HTTP mới được phân phối với Java 9 nhưng là một phần của mô đun Vườn ươm có tên jdk.incubator.httpclient. Các mô-đun ươm tạo là một phương tiện đưa các API không phải là cuối cùng vào tay các nhà phát triển trong khi các API tiến tới hoàn thiện hoặc loại bỏ trong một bản phát hành trong tương lai.

Trong Java 9, bạn có thể gửi một GETyêu cầu như:

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

Sau đó, bạn có thể kiểm tra trả lại HttpResponse:

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

Vì Máy khách HTTP mới này nằm trong java.httpclient jdk.incubator.httpclientmô-đun, bạn nên khai báo sự phụ thuộc này trong module-info.javatệp của bạn :

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

1
Cập nhật thêm: mô-đun hết trạng thái ủ. Bây giờ là java.net.http , không phải jdk.incubator.httpclient.
VGR

17

Ban đầu tôi đã bị đánh lừa bởi bài viết này ủng hộ HttpClient.

Sau đó tôi đã nhận ra rằng HttpURLConnectionsẽ ở lại từ bài viết này

Theo blog của Google :

Máy khách HTTP Apache có ít lỗi hơn trên Eclair và Froyo. Nó là sự lựa chọn tốt nhất cho các bản phát hành này. Đối với Gingerbread, kết nối httpURLC là sự lựa chọn tốt nhất. API đơn giản và kích thước nhỏ của nó làm cho nó phù hợp tuyệt vời cho Android.

Nén trong suốt và bộ nhớ đệm đáp ứng làm giảm việc sử dụng mạng, cải thiện tốc độ và tiết kiệm pin. Các ứng dụng mới nên sử dụng kết nối httpURLC; đó là nơi chúng ta sẽ dành năng lượng của mình trong tương lai.

Sau khi đọc bài viết này và một số câu hỏi khác về dòng chảy, tôi tin chắc rằng HttpURLConnectionsẽ ở lại trong thời gian dài hơn.

Một số câu hỏi SE ủng hộ HttpURLConnections:

Trên Android, tạo một yêu cầu POST với dữ liệu Biểu mẫu được mã hóa URL mà không cần sử dụng UrlEncodingFormEntity

HttpPost hoạt động trong dự án Java, không phải trong Android


15

Ngoài ra còn có OkHttp , một ứng dụng HTTP có hiệu quả theo mặc định:

  • Hỗ trợ HTTP / 2 cho phép tất cả các yêu cầu đến cùng một máy chủ để chia sẻ một ổ cắm.
  • Nhóm kết nối giảm độ trễ yêu cầu (nếu HTTP / 2 không khả dụng).
  • GZIP trong suốt thu nhỏ kích thước tải xuống.
  • Phản hồi lưu trữ tránh mạng hoàn toàn cho các yêu cầu lặp lại.

Đầu tiên tạo một thể hiện của OkHttpClient:

OkHttpClient client = new OkHttpClient();

Sau đó, chuẩn bị GETyêu cầu của bạn :

Request request = new Request.Builder()
      .url(url)
      .build();

cuối cùng, sử dụng OkHttpClientđể gửi chuẩn bị Request:

Response response = client.newCall(request).execute();

Để biết thêm chi tiết, bạn có thể tham khảo tài liệu của OkHttp


14

Bạn cũng có thể sử dụng JdkRequesttừ jcabi-http (Tôi là nhà phát triển), công việc này hoàn toàn phù hợp với bạn, trang trí kết nối HTTPURLC, bắn các yêu cầu HTTP và phản hồi phân tích cú pháp, ví dụ:

String html = new JdkRequest("http://www.google.com").fetch().body();

Kiểm tra bài đăng trên blog này để biết thêm thông tin: http://www.yegor256.com/2014/04/11/jcabi-http-intro.html


1
Làm thế nào để bạn xử lý cookie?
Dejell

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.