Zunächst ein Haftungsausschluss: Die veröffentlichten Code-Schnipsel sind grundlegende Beispiele. Sie werden trivial behandeln müssen IOException
s und RuntimeException
s wie NullPointerException
, ArrayIndexOutOfBoundsException
und Konsorten selbst.
Vorbereiten
Wir müssen zuerst mindestens die URL und den Zeichensatz kennen. Die Parameter sind optional und hängen von den funktionalen Anforderungen ab.
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¶m2=%s",
URLEncoder.encode(param1, charset),
URLEncoder.encode(param2, charset));
Die Abfrageparameter müssen name=value
formatiert und von verkettet sein &
. Normalerweise würden Sie die Abfrageparameter auch mit dem angegebenen Zeichensatz per URL codierenURLEncoder#encode()
.
Das String#format()
ist nur zur Vereinfachung. Ich bevorzuge es, wenn ich den String-Verkettungsoperator +
mehr als zweimal benötigen würde .
Auslösen einer HTTP-GET- Anforderung mit (optional) Abfrageparametern
Es ist eine triviale Aufgabe. Dies ist die Standardanforderungsmethode.
URLConnection connection = new URL(url + "?" + query).openConnection();
connection.setRequestProperty("Accept-Charset", charset);
InputStream response = connection.getInputStream();
// ...
Jede Abfragezeichenfolge sollte mit der URL verkettet werden ?
. Der Accept-Charset
Header weist den Server möglicherweise darauf hin, in welcher Codierung sich die Parameter befinden. Wenn Sie keine Abfragezeichenfolge senden, können Sie den Accept-Charset
Header weglassen. Wenn Sie keine Überschriften festlegen müssen, können Sie sogar die URL#openStream()
Verknüpfungsmethode verwenden.
InputStream response = new URL(url).openStream();
// ...
In beiden Fällen wird die Methode aufgerufen , wenn die andere Seite a ist HttpServlet
, doGet()
und die Parameter stehen zur Verfügung HttpServletRequest#getParameter()
.
Zu Testzwecken können Sie den Antworttext wie folgt auf stdout drucken:
try (Scanner scanner = new Scanner(response)) {
String responseBody = scanner.useDelimiter("\\A").next();
System.out.println(responseBody);
}
Auslösen einer HTTP-POST- Anforderung mit Abfrageparametern
Durch Setzen von URLConnection#setDoOutput()
to wird true
die Anforderungsmethode implizit auf POST gesetzt. Der Standard-HTTP-POST wie bei Webformularen ist vom Typ, application/x-www-form-urlencoded
bei dem die Abfragezeichenfolge in den Anforderungshauptteil geschrieben wird.
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();
// ...
Hinweis: Wenn Sie möchten , dass ein HTML - Formular programmatisch unterbreiten, nicht zu vergessen die nehmen name=value
Paare von irgendwelchen <input type="hidden">
Elementen in die Query - String und natürlich auch das name=value
Paar aus dem <input type="submit">
Element , das Sie zu „drücken“ programmatisch möchten (weil Dies wird normalerweise auf der Serverseite verwendet, um zu unterscheiden, ob und welche Taste gedrückt wurde.
Sie können auch die erhaltenen Guss URLConnection
zu HttpURLConnection
verwenden , das HttpURLConnection#setRequestMethod()
statt. Aber wenn Sie versuchen , die Verbindung für die Ausgabe zu verwenden , müssen Sie noch Satz URLConnection#setDoOutput()
zu true
.
HttpURLConnection httpConnection = (HttpURLConnection) new URL(url).openConnection();
httpConnection.setRequestMethod("POST");
// ...
In beiden Fällen wird die Methode aufgerufen , wenn die andere Seite a ist HttpServlet
, doPost()
und die Parameter stehen zur Verfügung HttpServletRequest#getParameter()
.
Die HTTP-Anfrage wird tatsächlich ausgelöst
Sie können die HTTP-Anforderung explizit mit URLConnection#connect()
auslösen, die Anforderung wird jedoch automatisch bei Bedarf ausgelöst, wenn Sie Informationen zur HTTP-Antwort abrufen möchten, z. B. den verwendeten Antworttext URLConnection#getInputStream()
usw. Die obigen Beispiele machen genau das, so dass der connect()
Aufruf tatsächlich überflüssig ist.
Sammeln von HTTP-Antwortinformationen
HTTP-Antwortstatus :
Du brauchst ein HttpURLConnection
hier. Wirf es zuerst, wenn nötig.
int status = httpConnection.getResponseCode();
HTTP-Antwortheader :
for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) {
System.out.println(header.getKey() + "=" + header.getValue());
}
HTTP-Antwortcodierung :
Wenn das Content-Type
einen charset
Parameter enthält , ist der Antworttext wahrscheinlich textbasiert und wir möchten den Antworttext dann mit der serverseitig angegebenen Zeichencodierung verarbeiten.
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.
}
Sitzung pflegen
Die serverseitige Sitzung wird normalerweise durch ein Cookie unterstützt. Einige Webformulare erfordern, dass Sie angemeldet sind und / oder von einer Sitzung verfolgt werden. Sie können die CookieHandler
API verwenden, um Cookies zu verwalten. Sie müssen ein CookieManager
mit einem CookiePolicy
von vorbereiten, ACCEPT_ALL
bevor Sie alle HTTP-Anforderungen senden.
// 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();
// ...
Beachten Sie, dass dies bekanntermaßen nicht immer unter allen Umständen ordnungsgemäß funktioniert. Wenn dies für Sie fehlschlägt, sollten Sie die Cookie-Header am besten manuell erfassen und festlegen. Grundsätzlich müssen Sie alle Set-Cookie
Header aus der Antwort des Logins oder der ersten GET
Anforderung abrufen und diese dann durch die nachfolgenden Anforderungen weiterleiten.
// 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]);
}
// ...
Das split(";", 2)[0]
wird bekommen Cookie Attribute befreien , die wie für die Server - Seite nicht relevant sind expires
, path
usw. Alternativ können Sie auch nutzen könnten cookie.substring(0, cookie.indexOf(';'))
statt split()
.
Streaming-Modus
Das HttpURLConnection
wird standardmäßig puffer den gesamten Körper Anforderung bevor es tatsächlich zu senden, unabhängig davon , ob Sie eine feste Inhaltslänge festgelegt haben , sie mit connection.setRequestProperty("Content-Length", contentLength);
. Dies kann zu OutOfMemoryException
s führen, wenn Sie gleichzeitig große POST-Anforderungen senden (z. B. Dateien hochladen). Um dies zu vermeiden, möchten Sie die einstellen HttpURLConnection#setFixedLengthStreamingMode()
.
httpConnection.setFixedLengthStreamingMode(contentLength);
Wenn die Länge des Inhalts jedoch vorher nicht wirklich bekannt ist, können Sie den Chunked-Streaming-Modus verwenden, indem Sie den HttpURLConnection#setChunkedStreamingMode()
entsprechenden Wert einstellen . Dadurch wird der HTTP- Transfer-Encoding
Header festgelegt, an den chunked
das Senden des Anforderungshauptteils in Blöcken erzwungen wird. Im folgenden Beispiel wird der Body in Blöcken von 1 KB gesendet.
httpConnection.setChunkedStreamingMode(1024);
User-Agent
Es kann vorkommen, dass eine Anfrage eine unerwartete Antwort zurückgibt, während dies mit einem echten Webbrowser problemlos funktioniert . Die Serverseite blockiert wahrscheinlich Anforderungen basierend auf dem User-Agent
Anforderungsheader. Das URLConnection
Testament legt standardmäßig fest, Java/1.6.0_19
wo der letzte Teil offensichtlich die JRE-Version ist. Sie können dies wie folgt überschreiben:
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.
Verwenden Sie die User-Agent-Zeichenfolge eines aktuellen Browsers .
Fehlerbehandlung
Wenn der HTTP-Antwortcode 4nn
(Clientfehler) oder 5nn
(Serverfehler) lautet , möchten Sie möglicherweise den lesen, HttpURLConnection#getErrorStream()
um festzustellen, ob der Server nützliche Fehlerinformationen gesendet hat.
InputStream error = ((HttpURLConnection) connection).getErrorStream();
Wenn der HTTP-Antwortcode -1 ist, ist bei der Verbindung und der Antwortbehandlung ein Fehler aufgetreten. Die HttpURLConnection
Implementierung in älteren JREs ist etwas fehlerhaft, da die Verbindungen am Leben bleiben. Sie können es deaktivieren, indem Sie die http.keepAlive
Systemeigenschaft auf setzen false
. Sie können dies zu Beginn Ihrer Bewerbung programmgesteuert tun, indem Sie:
System.setProperty("http.keepAlive", "false");
Dateien hochladen
Normalerweise verwenden Sie die multipart/form-data
Codierung für gemischte POST-Inhalte (Binär- und Zeichendaten). Die Codierung wird ausführlicher in RFC2388 beschrieben .
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();
}
Wenn die andere Seite a ist HttpServlet
, wird ihre doPost()
Methode aufgerufen und die Teile sind verfügbar von HttpServletRequest#getPart()
(beachten Sie, also nicht getParameter()
und so weiter!). Die getPart()
Methode ist jedoch relativ neu und wurde in Servlet 3.0 (Glassfish 3, Tomcat 7 usw.) eingeführt. Vor Servlet 3.0 verwenden Sie am besten Apache Commons FileUpload , um eine multipart/form-data
Anforderung zu analysieren . In dieser Antwort finden Sie auch Beispiele für die Ansätze FileUpload und Servelt 3.0.
Umgang mit nicht vertrauenswürdigen oder falsch konfigurierten HTTPS-Sites
Manchmal müssen Sie eine HTTPS-URL verbinden, möglicherweise weil Sie einen Web-Scraper schreiben. In diesem Fall kann es javax.net.ssl.SSLException: Not trusted server certificate
auf einigen HTTPS-Sites zu Problemen kommen, die ihre SSL-Zertifikate nicht auf dem neuesten Stand halten, java.security.cert.CertificateException: No subject alternative DNS name matching [hostname] found
oder javax.net.ssl.SSLProtocolException: handshake alert: unrecognized_name
auf einigen oder auf einigen falsch konfigurierten HTTPS-Sites.
Der folgende einmalige static
Initialisierer in Ihrer Web-Scraper-Klasse sollte HttpsURLConnection
die HTTPS-Sites milder gestalten und daher diese Ausnahmen nicht mehr auslösen.
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);
}
}
Letzte Worte
Der Apache HttpComponents HttpClient ist in dieser Hinsicht viel praktischer :)
HTML analysieren und extrahieren
Wenn Sie nur Daten aus HTML analysieren und extrahieren möchten, verwenden Sie besser einen HTML-Parser wie Jsoup
Ask Question
Taste oben rechts.--
Teil ist nicht Teil der Grenze selbst. Es ist nur eine Trennzeichenfolge. Ich habe Ihre ungültige Bearbeitung zurückgesetzt.HttpClient
jetzt zu verwenden undHttpURLConnection
grausam ist. android-developers.blogspot.in/2011/09/…Wenn Sie mit HTTP arbeiten, ist es fast immer nützlicher, auf
HttpURLConnection
die Basisklasse zu verweisenURLConnection
(daURLConnection
es sich um eine abstrakte Klasse handelt, wenn SieURLConnection.openConnection()
nach einer HTTP-URL fragen , erhalten Sie diese ohnehin zurück).Dann können Sie sich nicht darauf verlassen
URLConnection#setDoOutput(true)
, die Anforderungsmethode implizit auf POST zu setzen, sondern dies tun,httpURLConnection.setRequestMethod("POST")
was einige möglicherweise natürlicher finden (und Sie können auch andere Anforderungsmethoden wie PUT , DELETE , ... angeben ).Es bietet auch nützliche HTTP-Konstanten, damit Sie Folgendes tun können:
quelle
setDoOutput()
zutrue
sonst eine Ausnahme (auch wenn Sie geworfensetRequestMethod("POST")
). Um klar zu sein: EinstellungURLConnection#setDoOutput(true)
zutrue
implizit setzt die Anforderungsmethode POST, aber EinstellunghttpURLConnection.setRequestMethod("POST")
zu POST ist nicht implizit gesetztsetDoOutput()
zutrue
.Inspiriert von dieser und anderen Fragen zu SO habe ich einen minimalen Open-Source- Basic-http-Client erstellt , der die meisten der hier beschriebenen Techniken verkörpert.
google-http-java-client ist auch eine großartige Open-Source-Ressource.
quelle
Ich schlage vor, Sie werfen einen Blick auf den Code auf kevinsawicki / http-request. Es handelt sich im Grunde genommen um einen Wrapper, der
HttpUrlConnection
eine viel einfachere API bietet, falls Sie die Anfragen jetzt nur stellen möchten, oder Sie können sich die Quellen ansehen ( Es ist nicht zu groß, um einen Blick darauf zu werfen, wie Verbindungen behandelt werden.Beispiel: Stellen Sie eine
GET
Anfrage mit Inhaltstypapplication/json
und einigen Abfrageparametern:quelle
Es gibt zwei Optionen, die Sie für HTTP-URL-Treffer verwenden können: GET / POST
GET Anfrage: -
POST-Anfrage: -
quelle
Diese Antwort hat mich auch sehr inspiriert.
Ich bin oft in Projekten, in denen ich etwas HTTP ausführen muss, und ich möchte möglicherweise nicht viele Abhängigkeiten von Drittanbietern einbringen (die andere einbringen und so weiter und so fort usw.).
Ich fing an, meine eigenen Dienstprogramme zu schreiben, basierend auf einigen dieser Konversationen (keine, wo getan):
Dann gibt es nur ein paar oder statische Methoden.
Dann poste ...
Nun, Sie haben die Idee ...
Hier sind die Tests:
Den Rest finden Sie hier:
https://github.com/RichardHightower/boon
Mein Ziel ist es, die üblichen Dinge, die man tun möchte, etwas einfacher zu machen als ...
quelle
doPost
Methode einencharset
Parameter gibt, der zum Festlegen des Anforderungsheaders verwendet wird, aber dann werden die Daten mit einem fest codierten Zeichensatz geschriebenIO.CHARSET
. Ein Käfer?Aktualisieren
In Java 9 können Sie eine
GET
Anfrage senden wie:Dann können Sie die zurückgegebenen überprüfen
HttpResponse
:Da ist dieser neue HTTP Client in
java.httpclient
jdk.incubator.httpclient
Modul sollten Sie diese Abhängigkeit in Ihrermodule-info.java
Datei deklarieren :quelle
Anfangs wurde ich durch diesen Artikel irregeführt, der bevorzugt
HttpClient
.Später wurde mir klar, dass
HttpURLConnection
dies von diesem Artikel abweichen wirdLaut Google Blog :
Nachdem ich diesen Artikel und einige andere Fragen zum Stapelüberlauf gelesen habe , bin ich überzeugt, dass dies
HttpURLConnection
länger dauern wird.Einige der SE-Fragen befürworten
HttpURLConnections
:Stellen Sie unter Android eine POST-Anfrage mit URL-codierten Formulardaten, ohne UrlEncodedFormEntity zu verwenden
HttpPost funktioniert in Java-Projekten, nicht in Android
quelle
Es gibt auch OkHttp , einen HTTP-Client, der standardmäßig effizient ist:
Erstellen Sie zunächst eine Instanz von
OkHttpClient
:Dann bereiten Sie Ihre
GET
Anfrage vor:Schließlich verwenden
OkHttpClient
, um vorbereitet zu sendenRequest
:Weitere Informationen finden Sie in der Dokumentation zu OkHttp
quelle
Sie können auch verwenden ,
JdkRequest
von jcabi-http (Ich bin ein Entwickler), die für Sie alle funktionierts, Dekorieren HttpURLConnection, Brennen HTTP - Anfragen und -Antworten Parsen, zum Beispiel:Weitere Informationen finden Sie in diesem Blogbeitrag: http://www.yegor256.com/2014/04/11/jcabi-http-intro.html
quelle
Wenn Sie http get verwenden, entfernen Sie bitte diese Zeile
quelle