HTTP isteklerini tetiklemek ve işlemek için java.net.URLConnection nasıl kullanılır?


1948

Burada kullanımı java.net.URLConnectionsıklıkla sorulur ve Oracle öğreticisi bu konuda çok özlüdür.

Bu öğretici temel olarak yalnızca bir GET isteğinin nasıl tetikleneceğini ve yanıtın nasıl okunacağını gösterir. POST isteği gerçekleştirmek, istek başlıklarını ayarlamak, yanıt başlıklarını okumak, çerezlerle uğraşmak, HTML formu göndermek, dosya yüklemek vb.

Peki, java.net.URLConnection"gelişmiş" HTTP isteklerini tetiklemek ve işlemek için nasıl kullanabilirim ?

Yanıtlar:


2711

Öncelikle bir feragatname: yayınlanan kod parçacıklarının hepsi temel örneklerdir. Sen önemsiz işlemek gerekir IOExceptions ve RuntimeExceptionbenzeri s NullPointerException, ArrayIndexOutOfBoundsExceptionve eşlerinden kendin.


hazırlama

Önce en azından URL'yi ve karakter kümesini bilmemiz gerekir. Parametreler isteğe bağlıdır ve fonksiyonel gereksinimlere bağlıdır.

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

Sorgu parametreleri name=valueformatta olmalı ve ile birleştirilmelidir &. Ayrıca normalde kullanarak sorgu parametrelerini belirtilen karakter kümesiyle URL olarak kodlarsınız URLEncoder#encode().

String#format()Sadece kolaylık içindir. Ben +iki kez daha fazla String birleştirme operatörü gerektiğinde tercih ederim .


Bir Atış HTTP GET (isteğe bağlı olarak) sorgu parametreleriyle isteği

Bu önemsiz bir görev. Varsayılan istek yöntemidir.

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

Herhangi bir sorgu dizesi kullanılarak URL ile birleştirilmelidir ?. Accept-CharsetEğer herhangi bir sorgu dizesi göndermezseniz başlığı. İçindedir parametreleri kodlama neyi sunucuyu ipucu olabilir o zaman bırakabilirsiniz Accept-Charsetuzakta başlığı. Üstbilgi ayarlamanız gerekmiyorsa, URL#openStream()kısayol yöntemini bile kullanabilirsiniz .

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

Her iki durumda da, diğer taraf a ise HttpServlet, doGet()yöntemi çağrılır ve parametreler tarafından kullanılabilir HttpServletRequest#getParameter().

Test amacıyla, yanıt gövdesini stdout'a aşağıdaki gibi yazdırabilirsiniz:

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

Sorgu parametreleriyle bir HTTP POST isteği tetikleme

Ayarlama URLConnection#setDoOutput()için trueörtülü POST için istek yöntemini ayarlar. Web formları gibi standart HTTP POST application/x-www-form-urlencoded, sorgu dizesinin istek gövdesine yazıldığı türdedir.

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

Not: Programlı olarak bir HTML formu göndermek istediğinizde name=value, herhangi bir <input type="hidden">öğenin çiftini sorgu dizesine ve elbette programlı olarak "basmak" istediğiniz öğenin name=valueçiftini de almayı unutmayın <input type="submit">(çünkü genellikle sunucu tarafında bir düğmeye basıldığını ve varsa hangisini) ayırt etmek için kullanılır.

Ayrıca elde edilen yayın yapabiliyorsa URLConnectioniçin HttpURLConnectionve kullanımını HttpURLConnection#setRequestMethod()yerine. Ancak bağlantıyı çıkış için kullanmaya çalışıyorsanız, ayarlamanız URLConnection#setDoOutput()gerekir true.

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

Her iki durumda da, diğer taraf a ise HttpServlet, doPost()yöntemi çağrılır ve parametreler tarafından kullanılabilir HttpServletRequest#getParameter().


Aslında HTTP isteğini tetikliyor

HTTP isteğini açıkça kullanarak tetikleyebilirsiniz URLConnection#connect(), ancak HTTP yanıtıyla ilgili yanıt gövdesi gibi bilgileri almak istediğinizde, istek otomatik olarak istek üzerine tetiklenir URLConnection#getInputStream(). Yukarıdaki örnekler tam olarak bunu yapar, bu nedenle connect()çağrı aslında gereksizdir.


HTTP yanıt bilgilerini toplama

  1. HTTP yanıt durumu :

    HttpURLConnectionBurada bir yere ihtiyacın var. Gerekirse önce yayınlayın.

    int status = httpConnection.getResponseCode();
  2. HTTP yanıt başlıkları :

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

    Bir parametre Content-Typeiçerdiğinde charset, yanıt gövdesi büyük olasılıkla metin tabanlıdır ve yanıt gövdesini sunucu tarafında belirtilen karakter kodlamasıyla işlemek istiyoruz.

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

Oturumun sürdürülmesi

Sunucu tarafı oturumu genellikle bir çerez tarafından desteklenir. Bazı web formları, oturum açmanızı ve / veya bir oturum tarafından izlenmenizi gerektirir. CookieHandlerÇerezleri korumak için API'yı kullanabilirsiniz . Bir hazırlamak için gereken CookieManagerbir ile CookiePolicyait ACCEPT_ALLtüm HTTP isteklerini göndermeden önce.

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

Bunun her koşulda her zaman düzgün çalışmadığı biliniyor. Sizin için başarısız olursa, en iyisi çerez başlıklarını manuel olarak toplamak ve ayarlamaktır. Temel Set-Cookieolarak, oturum açma veya ilk GETisteğin yanıtından tüm başlıkları almanız ve bunu sonraki isteklerden geçirmeniz gerekir.

// 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]Gibi sunucu tarafı için alakasız çerez niteliklerin orada kurtulmak için olduğu expires, pathvb Alternatif olarak, kullanabilirsiniz cookie.substring(0, cookie.indexOf(';'))yerine split().


Akış modu

HttpURLConnectionVarsayılan olarak irade tampon tüm aslında kullanıyor olursanız sabit içerik uzunluk kendinizi ayarladığınız bakılmaksızın, göndermeden önce istek gövdesini connection.setRequestProperty("Content-Length", contentLength);. OutOfMemoryExceptionEşzamanlı olarak büyük POST istekleri (örneğin dosya yükleme) gönderdiğinizde bu durum neden olabilir . Bundan kaçınmak için HttpURLConnection#setFixedLengthStreamingMode(),.

httpConnection.setFixedLengthStreamingMode(contentLength);

Ancak içerik uzunluğu önceden önceden bilinmiyorsa, HttpURLConnection#setChunkedStreamingMode()buna göre ayarlayarak yığınlanmış akış modundan yararlanabilirsiniz . Bu , istek gövdesinin parçalar halinde gönderilmesini zorlayacak HTTP Transfer-Encodingüstbilgisini ayarlar chunked. Aşağıdaki örnek, gövdeyi 1 KB boyutlarında gönderecektir.

httpConnection.setChunkedStreamingMode(1024);

User-Agent

O gerçekleşebilir gerçek bir web tarayıcısı ile para cezası çalışırken, bir istek beklenmeyen bir yanıt verir . Sunucu tarafı büyük olasılıkla User-Agentistek başlığına göre istekleri engelliyor . URLConnectionVarsayılan olarak irade olarak ayarlayın Java/1.6.0_19son bölüm besbelli JRE sürümü olduğu. Bunu aşağıdaki gibi geçersiz kılabilirsiniz:

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.

Son kullanılan bir tarayıcıdan User-Agent dizesini kullanın .


Hata yönetimi

HTTP yanıt kodu 4nn(İstemci Hatası) veya 5nn(Sunucu Hatası) HttpURLConnection#getErrorStream()ise, sunucunun yararlı bir hata bilgisi gönderip göndermediğini görmek için simgesini okumak isteyebilirsiniz .

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

HTTP yanıt kodu -1 ise, bağlantı ve yanıt işleme ile ilgili bir sorun oluştu. HttpURLConnectionUygulama eski JRE canlı bağlantıları tutmak ile biraz adamcağız. http.keepAliveSystem özelliğini olarak ayarlayarak kapatmak isteyebilirsiniz false. Bunu uygulamanızın başlangıcında programlı olarak şu şekilde yapabilirsiniz:

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

Dosya yükleme

Normalde multipart/form-datakarışık POST içeriği (ikili ve karakter verileri) için kodlama kullanırsınız . Kodlama, RFC2388'de daha ayrıntılı olarak açıklanmaktadır .

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

Diğer taraf a ise HttpServlet, doPost()yöntemi çağrılır ve parçalar tarafından kullanılabilir HttpServletRequest#getPart()(not, bu yüzden değil getParameter() vb!). Ancak getPart()yöntem nispeten yenidir, Servlet 3.0'da (Glassfish 3, Tomcat 7, vb.) Servlet 3.0'dan önce, en iyi seçiminiz bir isteği ayrıştırmak için Apache Commons FileUpload'u kullanmaktır multipart/form-data. Ayrıca , FileUpload ve Servelt 3.0 yaklaşımlarının örnekleri için bu yanıta bakın .


Güvenilmeyen veya yanlış yapılandırılmış HTTPS siteleriyle ilgilenme

Bazen bir HTTPS URL'si bağlamanız gerekebilir, belki de bir web kazıyıcı yazdığınız için. Bu durumda, javax.net.ssl.SSLException: Not trusted server certificateSSL sertifikalarını güncel tutmayan bazı HTTPS sitelerinde veya bir java.security.cert.CertificateException: No subject alternative DNS name matching [hostname] foundveya javax.net.ssl.SSLProtocolException: handshake alert: unrecognized_nameyanlış yapılandırılmış HTTPS sitelerinde bir ile karşılaşabilirsiniz.

staticWeb kazıyıcı sınıfınızdaki aşağıdaki bir kerelik çalıştırıcı başlatıcısı, HttpsURLConnectionbu HTTPS siteleri için daha yumuşak olmalıdır ve bu nedenle artık bu istisnaları atmamalıdır .

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

Son sözler

Apaçi HttpComponents HttpClient olduğunu çok daha uygun bu tümünde :)


HTML'yi ayrıştırma ve ayıklama

İstediğiniz tek şey HTML'den veri ayrıştırmak ve çıkarmaksa, Jsoup gibi bir HTML ayrıştırıcısını daha iyi kullanın


119
Önce apache bağlantısını yerleştirmelisiniz, böylece bir çözüm arayan insanlar daha hızlı
bulmalı

40
@ivanceras: Bu yanıttaki bilgilere dayanarak kaynatamazsanız, lütfen Ask Questionsağ üstteki düğmeye basın .
BalusC

3
@Brais: Lütfen teknik özellikleri okuyun. --Bölüm sınırının kendisinin bir parçası değildir. Bu sadece bir ayırıcı dizgidir. Geçersiz düzenlemenizi geri aldım.
BalusC

7
@BalusC böyle mükemmel bir öğretici için çok teşekkürler. Lütfen "Akışları / bağlantıları kapatma" gibi bir başlık da ekleyin. Gerçekten ne zaman ve hangi akışların / bağlantıların kapatılacağı konusunda kafam karıştı.

10
Üzücü yanı Android'de olmasıdır değil Apache kullanılması önerilir HttpClientşimdi ve HttpURLConnectionacımasızdır. android-developers.blogspot.in/2011/09/…
yati sagade

91

HTTP ile çalışırken HttpURLConnection, temel sınıftan ziyade başvurmak neredeyse her zaman daha yararlıdır URLConnection(çünkü bir HTTP URL'sinde istediğinizde yine de alacağınız URLConnectionsoyut bir sınıftır URLConnection.openConnection()).

Daha sonra URLConnection#setDoOutput(true), istek yöntemini dolaylı olarak POST olarak ayarlamak yerine, httpURLConnection.setRequestMethod("POST")bazılarının daha doğal bulabileceği (ve ayrıca PUT , DELETE , ... gibi diğer istek yöntemlerini belirtmenize izin veren ) yapın.

Ayrıca şunları yapabilmeniz için yararlı HTTP sabitleri sağlar:

int responseCode = httpURLConnection.getResponseCode();

if (responseCode == HttpURLConnection.HTTP_OK) {

1
setDoOutPut true, GET'imi POST'a ayarlayan sorunumdu. Teşekkürler
Patrick Kafka

22
Eğer OutputStream veri yazmak için çalışıyorsanız gerekir hala ayarlamak setDoOutput()için trueaksi bir istisnası atılır (hatta eğer setRequestMethod("POST")). Açıklamak gerekirse: ayar URLConnection#setDoOutput(true)için trueörtülü olarak POST için istek yöntemini ayarlar ancak ayar httpURLConnection.setRequestMethod("POST")POST için yok değil örtülü set setDoOutput()için true.
Tony Chan

54

Bu ve SO ile ilgili diğer sorulardan esinlenerek, burada bulunan tekniklerin çoğunu içeren minimal açık kaynaklı bir basic-http-client oluşturdum.

google-http-java-client aynı zamanda harika bir açık kaynak kaynağıdır.


Ben de aynısını düşünüyordum. Ancak aynı zamanda, burada gösterildiği gibi bir HTTP GET, POST, vb. Yapmak için daha basit yöntemlerle kodlayan URLConnection kodunu kullanan bir barebone / basit Java kütüphanesine sahip olmak da güzel olabilir. Kütüphane daha sonra JAR ve harici JAR'lar istenmiyorsa, Java kodunda veya kaynak sınıf dosyasında içe aktarılan / kullanılan Java projesine dahil edilebilir. Bu, Apache vb. Gibi diğer kütüphanelerle yapılabilir, ancak URLConnection kullanan basit bir 1 dosya sınıfı kütüphaneye kıyasla daha acı vericidir.
David


24

Kevinsawicki / http-request üzerindeki koda bir göz atmanızı öneririm , temelde üstündeki bir sarıcı, HttpUrlConnectionşu anda istekleri yapmak istediğinizde veya kaynaklara bir göz atabildiğinizde çok daha basit bir API sağlar. çok büyük değil) bağlantıların nasıl ele alındığına bir göz atın.

Örnek: GETİçerik türü application/jsonve bazı sorgu parametreleriyle istekte bulunun:

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

HTTP URL İsabetleri ile gidebileceğiniz 2 seçenek vardır: GET / POST

İstek Al: -

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 talebi: -

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
Gerçek JSON yanıtını nasıl görüntüleyebilirsiniz?
Sora

21

Bu yanıttan da çok ilham aldım.

Sık sık HTTP yapmam gereken projelerdeyim ve çok sayıda 3. taraf bağımlılığı getirmek istemeyebilirim (bu da başkalarını getirir, vb.)

Bu konuşmanın bazılarını temel alarak kendi yardımcı programlarımı yazmaya başladım (hiçbir yerde yapılmadı):

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 {

Sonra sadece bir demet veya statik yöntemler var.

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

Sonra gönderin ...

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

Fikri anladınız ...

İşte testler:

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


}

Gerisini burada bulabilirsiniz:

https://github.com/RichardHightower/boon

Amacım o zaman biraz daha kolay bir şekilde yapmak istediğiniz ortak şeyleri sağlamaktır ....


2
doPostYöntemde charset, istek üstbilgisini ayarlamak için kullanılan bir parametrenin olması tuhaftır , ancak daha sonra veriler bazı sabit kodlu karakter kümesiyle yazılır IO.CHARSET. Bir böcek?
Vit Khudenko

21

Güncelleme

Yeni HTTP İstemcisi, Java 9 ile birlikte, ancak adlı bir Incubator modülünün parçası olarak gönderildi jdk.incubator.httpclient. İnkübatör modülleri, API'lar gelecekteki bir sürümde sonlandırma veya kaldırma yönünde ilerlerken nihai olmayan API'leri geliştiricilerin eline geçirmenin bir yoludur.

Java 9'da aşağıdaki GETgibi bir istek gönderebilirsiniz :

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

Sonra iade edilenleri inceleyebilirsiniz HttpResponse:

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

Bu yeni HTTP İstemcisi olduğu için java.httpclient jdk.incubator.httpclientmodülünde, module-info.javadosyanızda bu bağımlılığı beyan etmelisiniz :

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

1
İlave güncelleme: modül inkübasyon durumu dışında. Artık java.net.http , jdk.incubator.httpclient değil.
VGR

17

Başlangıçta bu makaleyi yanıltmıştım HttpClient.

Daha sonra HttpURLConnectionbu makaleden ayrılacağının farkına vardım

Google bloguna göre :

Apache HTTP istemcisinde Eclair ve Froyo üzerinde daha az hata var. Bu sürümler için en iyi seçimdir. Gingerbread için HttpURLConnection en iyi seçimdir. Basit API ve küçük boyutu, Android için mükemmel uyum sağlar.

Şeffaf sıkıştırma ve yanıt önbelleği ağ kullanımını azaltır, hızı artırır ve pil tasarrufu sağlar. Yeni uygulamalar HttpURLConnection kullanmalıdır; enerjimizi ileride harcayacağımız yer burası.

Bu makaleyi ve akış soruları üzerindeki diğer yığınları okuduktan sonra , bunun HttpURLConnectiondaha uzun süre kalacağına inanıyorum .

SE sorularının bazıları HttpURLConnections:

Android'de UrlEncodedFormEntity kullanmadan URL Kodlamalı Form verileriyle bir POST isteği yapın

HttpPost, Android'de değil, Java projesinde çalışır


15

Varsayılan olarak verimli olan bir HTTP istemcisi olan OkHttp de vardır :

  • HTTP / 2 desteği, aynı ana bilgisayardan gelen tüm isteklerin bir soketi paylaşmasına izin verir.
  • Bağlantı havuzu oluşturma istek gecikmesini azaltır (HTTP / 2 mevcut değilse).
  • Şeffaf GZIP indirme boyutlarını küçültür.
  • Yanıt önbelleğe alma, tekrar istekleri için ağı tamamen önler.

Önce aşağıdakilerin bir örneğini oluşturun OkHttpClient:

OkHttpClient client = new OkHttpClient();

Ardından, GETisteğinizi hazırlayın :

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

son olarak, OkHttpClienthazırlanan göndermek için kullanın Request:

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

Daha fazla ayrıntı için OkHttp belgelerine bakabilirsiniz.



Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.