Bagaimana cara menggunakan java.net.URLConnection untuk memecat dan menangani permintaan HTTP?

1948

Penggunaan java.net.URLConnectionditanya tentang cukup sering di sini, dan tutorial Oracle adalah terlalu ringkas tentang hal itu.

Tutorial itu pada dasarnya hanya menunjukkan cara menjalankan permintaan GET dan membaca respons. Itu tidak menjelaskan di mana saja bagaimana menggunakannya untuk antara lain melakukan permintaan POST, mengatur header permintaan, membaca header respons, menangani cookie, mengirimkan formulir HTML, mengunggah file, dll.

Jadi, bagaimana saya bisa java.net.URLConnectionmemecat dan menangani permintaan HTTP "lanjutan"?

BalusC
sumber

Jawaban:

2711

Pertama, penafian sebelumnya: cuplikan kode yang diposting adalah semua contoh dasar. Anda harus menangani hal-hal sepele IOExceptiondan RuntimeExceptionseperti itu NullPointerException, ArrayIndexOutOfBoundsExceptiondan mendampingi diri Anda sendiri.


Mempersiapkan

Pertama-tama kita perlu tahu setidaknya URL dan rangkaian karakter. Parameter bersifat opsional dan tergantung pada persyaratan fungsional.

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

Parameter kueri harus dalam name=valueformat dan disatukan oleh &. Anda biasanya juga akan menyandi URL parameter kueri dengan menggunakan rangkaian karakter yang ditentukan URLEncoder#encode().

The String#format()hanya untuk kenyamanan. Saya lebih suka ketika saya akan membutuhkan operator penggabungan String +lebih dari dua kali.


Menembak a permintaan HTTP GET dengan parameter kueri (opsional)

Ini tugas yang sepele. Ini metode permintaan default.

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

Setiap string kueri harus digabungkan ke URL menggunakan ?. The Accept-Charsetsundulan mungkin petunjuk server apa pengkodean parameter dalam. Jika Anda tidak mengirim string kueri, maka Anda dapat meninggalkan Accept-Charsetsundulan jauh. Jika Anda tidak perlu mengatur header apa pun, maka Anda bahkan dapat menggunakan URL#openStream()metode pintas.

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

Either way, jika pihak lain adalah HttpServlet, maka doGet()metodenya akan dipanggil dan parameter akan tersedia oleh HttpServletRequest#getParameter().

Untuk tujuan pengujian, Anda dapat mencetak isi tanggapan ke stdout seperti di bawah ini:

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

Memecat permintaan HTTP POST dengan parameter kueri

Mengatur URLConnection#setDoOutput()agar truesecara implisit menetapkan metode permintaan ke POST. HTTP POST standar seperti yang dilakukan formulir web adalah jenis di application/x-www-form-urlencodedmana string kueri ditulis ke badan permintaan.

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

Catatan: setiap kali Anda ingin mengirimkan formulir HTML secara terprogram, jangan lupa untuk membawa name=valuepasangan <input type="hidden">elemen apa pun ke dalam string kueri dan tentu saja juga name=valuepasangan dari<input type="submit"> elemen yang ingin Anda "tekan" secara terprogram (karena yang biasanya digunakan di sisi server untuk membedakan jika tombol ditekan dan jika demikian, yang mana).

Anda juga dapat melemparkan yang diperoleh URLConnectionke HttpURLConnectiondan menggunakannya HttpURLConnection#setRequestMethod(). Tapi jika Anda mencoba untuk menggunakan koneksi untuk output Anda masih perlu untuk set URLConnection#setDoOutput()ke true.

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

Either way, jika pihak lain adalah HttpServlet, maka doPost()metodenya akan dipanggil dan parameter akan tersedia olehHttpServletRequest#getParameter().


Sebenarnya mengaktifkan permintaan HTTP

Anda dapat memecat permintaan HTTP secara eksplisit dengan URLConnection#connect(), tetapi permintaan akan secara otomatis dipecat saat diminta ketika Anda ingin mendapatkan informasi tentang respons HTTP, seperti badan respons yang menggunakan URLConnection#getInputStream()dan sebagainya. Contoh di atas tidak persis seperti itu, jadi connect()panggilan itu sebenarnya berlebihan.


Mengumpulkan informasi respons HTTP

  1. Status respons HTTP :

    Anda butuh di HttpURLConnectionsini. Keluarkan terlebih dahulu jika perlu.

    int status = httpConnection.getResponseCode();
  2. Header respons HTTP :

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

    Ketika Content-Typeberisi charsetparameter, maka badan respons kemungkinan berbasis teks dan kami ingin memproses badan respons dengan pengkodean karakter yang ditentukan sisi server.

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

Mempertahankan sesi

Sesi sisi server biasanya didukung oleh cookie. Beberapa formulir web mengharuskan Anda masuk dan / atau dilacak oleh suatu sesi. Anda dapat menggunakan CookieHandlerAPI untuk mengelola cookie. Anda perlu menyiapkan CookieManagerdengan CookiePolicydari ACCEPT_ALLsebelum mengirim semua permintaan 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();
// ...

Perhatikan bahwa ini diketahui tidak selalu berfungsi dengan baik dalam semua keadaan. Jika gagal untuk Anda, maka yang terbaik adalah mengumpulkan dan mengatur header cookie secara manual. Anda pada dasarnya perlu mengambil semua Set-Cookieheader dari respons login atau GETpermintaan pertama dan kemudian meneruskannya melalui permintaan berikutnya.

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

The split(";", 2)[0]apakah ada untuk menyingkirkan atribut cookies yang tidak relevan untuk sisi server seperti expires, path, dll Atau, Anda bisa juga menggunakan cookie.substring(0, cookie.indexOf(';'))bukan split().


Mode streaming

Secara HttpURLConnectiondefault, buffer akan meminta seluruh badan permintaan sebelum mengirimkannya, terlepas dari apakah Anda telah menetapkan sendiri panjang konten yang tetap digunakan connection.setRequestProperty("Content-Length", contentLength);. Ini dapat menyebabkan OutOfMemoryExceptionsetiap kali Anda secara bersamaan mengirim permintaan POST besar (misalnya mengunggah file). Untuk menghindari ini, Anda ingin mengatur HttpURLConnection#setFixedLengthStreamingMode().

httpConnection.setFixedLengthStreamingMode(contentLength);

Tetapi jika panjang konten benar-benar tidak diketahui sebelumnya, maka Anda dapat menggunakan mode streaming chunked dengan mengatur yang HttpURLConnection#setChunkedStreamingMode()sesuai. Ini akan mengatur Transfer-Encodingtajuk HTTP chunkedyang akan memaksa badan permintaan dikirim dalam bentuk potongan. Contoh di bawah ini akan mengirim tubuh dalam potongan 1KB.

httpConnection.setChunkedStreamingMode(1024);

Agen pengguna

Itu bisa terjadi bahwa permintaan mengembalikan respons yang tidak terduga, sementara itu berfungsi baik dengan browser web nyata . Sisi server mungkin memblokir permintaan berdasarkan User-Agentheader permintaan. Secara URLConnectiondefault akan mengaturnya ke Java/1.6.0_19mana bagian terakhir jelas merupakan versi JRE. Anda dapat menimpa ini sebagai berikut:

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.

Gunakan string User-Agent dari browser terbaru .


Menangani kesalahan

Jika kode respons HTTP adalah 4nn(Kesalahan Klien) atau 5nn(Kesalahan Server), maka Anda mungkin ingin membaca HttpURLConnection#getErrorStream()untuk melihat apakah server telah mengirim informasi kesalahan yang berguna.

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

Jika kode respons HTTP -1, maka ada yang salah dengan koneksi dan penanganan respons. The HttpURLConnectionimplementasi di JRE yang lebih tua agak buggy dengan menjaga koneksi hidup. Anda mungkin ingin mematikannya dengan menyetel http.keepAliveproperti sistem ke false. Anda dapat melakukan ini secara terprogram di awal aplikasi Anda dengan:

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

Mengunggah file

Anda biasanya menggunakan multipart/form-datapengodean untuk konten POST campuran (data biner dan karakter). Pengkodean lebih rinci dijelaskan dalam 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();
}

Jika sisi lain adalah a HttpServlet, maka doPost()metodenya akan dipanggil dan bagian-bagian akan tersedia oleh HttpServletRequest#getPart()(perhatikan, maka tidak getParameter() dan seterusnya!). Namun getPart()metode ini relatif baru, diperkenalkan di Servlet 3.0 (Glassfish 3, Tomcat 7, dll). Sebelum ke Servlet 3.0, pilihan terbaik Anda menggunakan Apache Commons FileUpload untuk mengurai multipart/form-datapermintaan. Lihat juga jawaban ini untuk contoh pendekatan FileUpload dan Servelt 3.0.


Berurusan dengan situs HTTPS yang tidak terpercaya atau salah konfigurasi

Terkadang Anda perlu menghubungkan URL HTTPS, mungkin karena Anda sedang menulis scraper web. Dalam hal itu, Anda mungkin menghadapi javax.net.ssl.SSLException: Not trusted server certificatebeberapa situs HTTPS yang tidak memperbarui sertifikat SSL mereka, atau a java.security.cert.CertificateException: No subject alternative DNS name matching [hostname] foundataujavax.net.ssl.SSLProtocolException: handshake alert: unrecognized_name pada beberapa situs HTTPS yang tidak terkonfigurasi.

Berikut-satu kali menjalankan staticinitializer di web kelas scraper Anda harus membuat HttpsURLConnectionlebih ringan untuk orang-orang HTTPS situs dan dengan demikian tidak membuang mereka pengecualian lagi.

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

Kata-kata terakhir

The Apache HttpComponents HttpClient adalah jauh lebih nyaman dalam ini semua :)


Parsing dan mengekstraksi HTML

Jika semua yang Anda inginkan adalah parsing dan mengekstraksi data dari HTML, maka lebih baik gunakan parser HTML seperti Jsoup

BalusC
sumber
119
Anda harus menempatkan tautan apache terlebih dahulu, sehingga orang yang mencari solusi menemukannya lebih cepat;)
ZeissS
40
@ivanceras: Jika Anda tidak dapat mengubahnya berdasarkan informasi dalam jawaban ini, maka silakan tekan Ask Questiontombol di kanan atas.
BalusC
3
@Brais: Baca spesifikasi. Bagian --itu bukan bagian dari batas itu sendiri. Itu hanya string pemisah. Saya telah memutar kembali hasil edit Anda yang tidak valid.
BalusC
7
@ BalusC terima kasih banyak untuk tutorial yang begitu sempurna. Harap sertakan tajuk seperti "Aliran / koneksi penutup". Saya benar-benar bingung tentang kapan dan mana aliran / koneksi untuk ditutup.
10
Bagian yang menyedihkan adalah bahwa di Android tidak disarankan untuk menggunakan Apache HttpClientsekarang dan HttpURLConnectionkejam. android-developers.blogspot.in/2011/09/...
yati sagade
91

Ketika bekerja dengan HTTP, hampir selalu lebih bermanfaat untuk merujuk HttpURLConnectiondaripada kelas dasar URLConnection(karena URLConnectionini adalah kelas abstrak ketika Anda meminta URLConnection.openConnection()pada URL HTTP itulah yang akan Anda dapatkan kembali).

Kemudian Anda dapat daripada mengandalkan URLConnection#setDoOutput(true)untuk secara implisit mengatur metode permintaan ke POST daripada melakukan httpURLConnection.setRequestMethod("POST")yang beberapa mungkin menemukan lebih alami (dan yang juga memungkinkan Anda untuk menentukan metode permintaan lainnya seperti PUT , DELETE , ...).

Ini juga menyediakan konstanta HTTP yang berguna sehingga Anda dapat melakukan:

int responseCode = httpURLConnection.getResponseCode();

if (responseCode == HttpURLConnection.HTTP_OK) {
Paal Thorstensen
sumber
1
setDoOutPut true adalah masalah saya yang mengatur GET saya menjadi POST. Terima kasih
Patrick Kafka
22
Jika Anda mencoba untuk menulis data ke outputstream Anda masih harus mengatur setDoOutput()untuk truedinyatakan pengecualian dilemparkan (bahkan jika Anda setRequestMethod("POST")). Untuk menjadi jelas: pengaturan URLConnection#setDoOutput(true)untuk truesecara implisit menetapkan metode permintaan ke POST, tetapi pengaturan httpURLConnection.setRequestMethod("POST")untuk POST tidak secara implisit diatur setDoOutput()ke true.
Tony Chan
54

Terinspirasi oleh ini dan pertanyaan lain pada SO, saya telah membuat open-source basic-http-client minimal yang mewujudkan sebagian besar teknik yang ditemukan di sini.

google-http-java-client juga merupakan sumber terbuka yang bagus.

David Chandler
sumber
Saya hanya berpikir yang sama. Tetapi mungkin juga menyenangkan untuk memiliki barebones / perpustakaan Java sederhana yang hanya menggunakan kode URLConnection seperti yang disajikan di sini yang merangkum kode untuk metode yang lebih sederhana untuk melakukan HTTP GET, POST, dll. Perpustakaan kemudian dapat dikompilasi & dikemas sebagai JAR dan diimpor / digunakan dalam kode Java atau file kelas sumber dapat dimasukkan dalam proyek Java jika JAR eksternal tidak diinginkan. Ini dapat dilakukan dengan perpustakaan lain seperti Apache, dll. Tetapi lebih menyusahkan dibandingkan dengan perpustakaan kelas file 1 sederhana menggunakan URLConnection.
David
rapidvaluesolutions.com/tech_blog/... lebih menyukai HttpURLConnection daripada HttpClient
Ravindra babu
24

Saya sarankan Anda melihat kode pada kevinsawicki / http-request , pada dasarnya pembungkus di atasHttpUrlConnection atasnya menyediakan API yang lebih sederhana jika Anda hanya ingin membuat permintaan sekarang atau Anda dapat melihat sumbernya ( tidak terlalu besar) untuk melihat bagaimana koneksi ditangani.

Contoh: Buat GETpermintaan dengan tipe konten application/jsondan beberapa parameter kueri:

// 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);
fernandohur
sumber
24

Ada 2 opsi yang dapat Anda gunakan dengan Hits URL HTTP: GET / POST

DAPATKAN Permintaan: -

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

Permintaan 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()));
Utkash Bhatt
sumber
3
Bagaimana Anda bisa melihat respons JSON yang sebenarnya?
Sora
21

Saya juga sangat terinspirasi oleh respons ini.

Saya sering mengerjakan proyek-proyek di mana saya perlu melakukan beberapa HTTP, dan saya mungkin tidak ingin membawa banyak dependensi pihak ke-3 (yang membawa orang lain dan seterusnya dan seterusnya, dll.)

Saya mulai menulis utilitas saya sendiri berdasarkan pada beberapa percakapan ini (tidak di mana pun dilakukan):

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 {

Lalu ada hanya banyak atau metode statis.

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

Kemudian poskan ...

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

Baik Anda mendapatkan ide ....

Inilah tes-tesnya:

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


}

Anda dapat menemukan sisanya di sini:

https://github.com/RichardHightower/boon

Tujuan saya adalah untuk menyediakan hal-hal umum yang ingin dilakukan dengan cara yang sedikit lebih mudah daripada ....

RickHigh
sumber
2
Sungguh aneh bahwa dalam doPostmetode ini ada charsetparam, yang digunakan untuk mengatur header permintaan, tetapi kemudian data ditulis dengan beberapa charset yang dikodekan keras IO.CHARSET. Bug?
Vit Khudenko
21

Memperbarui

Klien HTTP baru dikirim dengan Java 9 tetapi sebagai bagian dari modul Inkubator bernama jdk.incubator.httpclient. Modul inkubator adalah cara untuk menempatkan API non-final di tangan pengembang sementara API berkembang menuju finalisasi atau penghapusan dalam rilis yang akan datang.

Di Java 9, Anda dapat mengirim GETpermintaan seperti:

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

Kemudian Anda dapat memeriksa yang dikembalikan HttpResponse:

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

Karena Klien HTTP baru ini ada di java.httpclient jdk.incubator.httpclientmodul, Anda harus mendeklarasikan ketergantungan ini dalam module-info.javafile Anda :

module com.foo.bar {
    requires jdk.incubator.httpclient;
}
Ali Dehghani
sumber
1
Pembaruan lebih lanjut: modul keluar dari status inkubasi. Sekarang java.net.http , bukan jdk.incubator.httpclient.
VGR
17

Awalnya saya disesatkan oleh artikel ini yang nikmatHttpClient .

Kemudian saya menyadari bahwa saya HttpURLConnectionakan tinggal dari artikel ini

Sesuai blog Google :

Klien HTTP Apache memiliki lebih sedikit bug di Eclair dan Froyo. Ini adalah pilihan terbaik untuk rilis ini. Untuk Gingerbread, HttpURLConnection adalah pilihan terbaik. APInya yang sederhana dan ukurannya yang kecil membuatnya sangat cocok untuk Android.

Kompresi transparan dan caching respons mengurangi penggunaan jaringan, meningkatkan kecepatan, dan menghemat baterai. Aplikasi baru harus menggunakan HttpURLConnection; di sinilah kita akan menghabiskan energi kita untuk maju.

Setelah membaca artikel ini dan beberapa pertanyaan overflow, saya yakin ituHttpURLConnection akan bertahan untuk jangka waktu yang lebih lama.

Beberapa pertanyaan SE mendukung HttpURLConnections:

Di Android, buat permintaan POST dengan URL Data Formulir yang Dienkripsi tanpa menggunakan UrlEncodedFormEntity

HttpPost berfungsi di proyek Java, bukan di Android

Ravindra babu
sumber
15

Ada juga OkHttp , yang merupakan klien HTTP yang efisien secara default:

  • Dukungan HTTP / 2 memungkinkan semua permintaan ke host yang sama untuk berbagi soket.
  • Penggabungan koneksi mengurangi latensi permintaan (jika HTTP / 2 tidak tersedia).
  • GZIP transparan menyusut ukuran unduhan.
  • Caching respons menghindari jaringan sepenuhnya untuk permintaan ulangi.

Pertama buat instance dari OkHttpClient:

OkHttpClient client = new OkHttpClient();

Kemudian, siapkan GETpermintaan Anda :

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

Akhirnya, gunakan OkHttpClientuntuk mengirim disiapkan Request:

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

Untuk detail lebih lanjut, Anda dapat membaca dokumentasi OkHttp

Ali Dehghani
sumber
14

Anda juga dapat menggunakan JdkRequestdari jcabi-http (saya seorang pengembang), yang melakukan semua ini untuk Anda, menghias HttpURLConnection, mem- request permintaan HTTP dan respons parsing, misalnya:

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

Lihat posting blog ini untuk info lebih lanjut: http://www.yegor256.com/2014/04/11/jcabi-http-intro.html

yegor256
sumber
1
Bagaimana Anda menangani cookie?
Dejell
12

jika Anda menggunakan http dapatkan tolong hapus baris ini

urlConnection.setDoOutput(true);
Nison Cheruvathur
sumber