Ich verstehe nicht, ob beim Aufrufen der Methode "getInputStream" von HttpUrlConnection eine "IOException" auftritt. Ausnahmestapelspuren können zurückgegeben werden.
TL;DR
Wenn in "HttpUrlConnection # getInputStream" eine "IOException" auftritt, wird die "IOException", die in dieser Instanz in der Vergangenheit aufgetreten ist, verschachtelt.
Angenommen, Sie haben folgenden Code:
import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpServer;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.URL;
public class HttpURLConnectionTest {
public static void main(String[] args) throws IOException {
// HTTP Server
HttpServer server = HttpServer.create(new InetSocketAddress(8087), 0);
server.createContext("/", exchange -> {
Headers headers = exchange.getResponseHeaders();
headers.add("Content-type", "text/plain");
String body = "error!";
byte[] bytes = body.getBytes();
exchange.sendResponseHeaders(400, bytes.length);
try (OutputStream out = exchange.getResponseBody()) {
out.write(bytes);
}
});
server.start();
System.out.println("start server.");
// HTTP Client
HttpURLConnection conn = null;
try {
URL url = new URL("http://localhost:8087/");
conn = (HttpURLConnection) url.openConnection();
conn.connect();
int responseCode = conn.getResponseCode();
System.out.println("responseCode = " + responseCode); // 400
try (InputStream in = conn.getInputStream()) { //Hier tritt eine Ausnahme auf
throw new AssertionError("Sollte nicht hierher kommen");
}
} catch (IOException e) {
e.printStackTrace(); //Ich erhalte eine Stapelverfolgung, die ich nicht verstehe
} finally {
if (conn != null) conn.disconnect();
server.stop(0);
}
}
}
Bei der Ausführung wird die folgende Stapelverfolgung angezeigt.
Folgendes wird auf der Konsole angezeigt.
start server.
responseCode = 400
java.io.IOException: Server returned HTTP response code: 400 for URL: http://localhost:8087/
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at sun.net.www.protocol.http.HttpURLConnection$10.run(HttpURLConnection.java:1950)
at sun.net.www.protocol.http.HttpURLConnection$10.run(HttpURLConnection.java:1945)
at java.security.AccessController.doPrivileged(Native Method)
at sun.net.www.protocol.http.HttpURLConnection.getChainedException(HttpURLConnection.java:1944)
at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1514)
at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1498)
at HttpURLConnectionTest.main(HttpURLConnectionTest.java:38)
Caused by: java.io.IOException: Server returned HTTP response code: 400 for URL: http://localhost:8087/
at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1900)
at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1498)
at java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:480)
at HttpURLConnectionTest.main(HttpURLConnectionTest.java:35)
Process finished with exit code 0
HttpURLConnection löst eine "IOException" oder "FileNotFoundException" aus, wenn "getInputStream" aufgerufen wird, wenn der Antwortstatuscode 400 oder höher ist.
if (respCode >= 400) {
if (respCode == 404 || respCode == 410) {
throw new FileNotFoundException(url.toString());
} else {
throw new java.io.IOException("Server returned HTTP" +
" response code: " + respCode + " for URL: " +
url.toString());
}
}
In diesem Fall kann der Antworttext nur gelesen werden, wenn Sie die Methode "getErrorStream" anstelle von "getInputStream" verwenden.
Es ist eine unglaubliche API, aber es funktioniert tatsächlich so.
Beim Hauptthema, der Stapelverfolgung, gibt es verschachtelte Ausnahmen.
Mit Blick auf die erste Ausnahme hat der Aufruf von "getInputStream" eine "IOException" ausgelöst, bei der es sich um eine Stapelverfolgung handelt, die mit den obigen Annahmen übereinstimmt.
Das Problem ist die verschachtelte Ausnahme.
Bei den verschachtelten Ausnahmen sieht es so aus, als ob die Ausnahme beim vorherigen Aufruf von "getResponseCode" anstelle von "getInputStream" ausgelöst wurde. In der Realität löst getResponseCode
jedoch keine Ausnahme aus und gibt den Statuscode 400 als Rückgabewert zurück.
Der Stack-Trace des Methodenaufrufs, der zwei Zeilen vor getInputStream
ausgeführt wurde, ist dann in getInputStream
verschachtelt. Dies ist ein Stack-Trace, der in der normalen Java-Programmierung nicht angezeigt wird.
Warum tritt eine solche Stapelverfolgung auf?
--getResponseCode
ruft intern getInputStream
auf (um den Statuscode zu lesen)
--getInputStream
löst eine IOException aus, wenn der Statuscode 400 oder höher ist
getResponseCode
löst die generierte IOException
nicht aus und gibt den Status als Rückgabewert zurück.Es wird im Quellcode so beschrieben.
/* Remembered Exception, we will throw it again if somebody
calls getInputStream after disconnect */
private Exception rememberedException = null;
Es heißt: "Wenn getInputStream nach dem Trennen aufgerufen wird, denken Sie daran, die Ausnahme erneut auszulösen."
Wenn eine vorherige Ausnahme aufgetreten ist, verschachteln Sie die vorherige Ausnahme in der neuen Ausnahme (verwenden Sie Throwable # initCause
).
private synchronized InputStream getInputStream0() throws IOException {
//Unterlassung
if (rememberedException != null) {
if (rememberedException instanceof RuntimeException)
throw new RuntimeException(rememberedException);
else {
throw getChainedException((IOException)rememberedException);
}
}
try {
final Object[] args = { rememberedException.getMessage() };
IOException chainedException = //...
//Unterlassung
//Verschachteln Sie frühere Ausnahmen hier
chainedException.initCause(rememberedException);
return chainedException;
} catch (Exception ignored) {
return rememberedException;
}
}
Aus diesem Grund wird die "IOException", die intern beim Starten des "getResponseCode" aufgetreten ist, gespeichert und beim Start des "getInputStream" als verschachtelte Ausnahme angezeigt.
Aus Sicht des API-Benutzers scheint "getResponseCode" erfolgreich zu sein, so dass es irrelevant erscheint, dass die nachfolgenden verschachtelten Ausnahmen in "getInputStream" den Stack-Trace des zuvor erfolgreichen "getResponseCode" enthalten.
Weil ich nicht wusste, dass ich getInputStream
und getErrorStream
richtig verwenden musste
Als ich mir diese Stapelspur ansah, ergab das keinen Sinn.
Wenn in "HttpUrlConnection # getInputStream" eine "IOException" auftritt, wird die "IOException", die in dieser Instanz in der Vergangenheit aufgetreten ist, verschachtelt.
Durch diese Forschung dachte ich, dass HttpUrlConnection die folgenden Probleme hat.
Viele Leute haben das API-Design von 1 in Frage gestellt.
Wenn Sie feststellen, dass das Verhalten in Java 11 und 13 identisch ist, wird davon ausgegangen, dass das Verhalten nicht geändert werden kann, um die Kompatibilität aufrechtzuerhalten. Das ist unvermeidlich, aber zumindest möchte ich, dass die API-Dokumentation verbessert wird.
Es ist keine einfach zu verwendende API, die ein Kompliment macht. Wenn Sie also keinen bestimmten Grund haben, z. B. die Verwendung einer Standard-API, scheint es besser, eine andere Bibliothek zu verwenden.