Errorstream in HttpUrlConnection
I want to do a POST request to an HTTP Servlet I wrote myself. Good case (HTTP response Code 200) always works开发者_开发百科 fine by using URL.openConnection() method. But when I receive a desired error response code (e.g. 400) then I thought I have to use HttpUrlConnection.getErrorStream(). But the ErrorStream object is null though I am sending data back from the servlet in error case (I want to evaluate this data to generate error messages). This is what my code looks like:
HttpURLConnection con = null;
try {
//Generating request String
String request = "request="+URLEncoder.encode(xmlGenerator.getStringFromDocument(xmlGenerator.generateConnectRequest(1234)),"UTF-8");
//Receiving HttpUrlConnection (DoOutput = true; RequestMethod is set to "POST")
con = openConnection();
if (con != null){
PrintWriter pw = new PrintWriter(con.getOutputStream());
pw.println(request);
pw.flush();
pw.close();
InputStream errorstream = con.getErrorStream();
BufferedReader br = null;
if (errorstream == null){
InputStream inputstream = con.getInputStream();
br = new BufferedReader(new InputStreamReader(inputstream));
}else{
br = new BufferedReader(new InputStreamReader(errorstream));
}
String response = "";
String nachricht;
while ((nachricht = br.readLine()) != null){
response += nachricht;
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
So my question is, why returns getErrorStream() null though status code is 400 (I can see it in the IOException that is thrown when it calls con.getInputStream())
Thanks
From the java documentation on getErrorStream():
Returns the error stream if the connection failed but the server sent useful data nonetheless. The typical example is when an HTTP server responds with a 404, which will cause a FileNotFoundException to be thrown in connect, but the server sent an HTML help page with suggestions as to what to do. This method will not cause a connection to be initiated. If the connection was not connected, or if the server did not have an error while connecting or if the server had an error but no error data was sent, this method will return null. This is the default.
So if you didn't get to the server (bad url for example) or the server didn't send anything in the response, getErrorStream() will return null.
InputStream inputStream = null;
try {
inputStream = connection.getInputStream();
} catch(IOException exception) {
inputStream = connection.getErrorStream();
}
It is like when you set response header status code as anything beyond 200, the connection object is reset. it will generate SocketTimeoutException while getting the inputstream but when it comes in the catch it gives you the inputstream anyway, what you are expecting.
Digging a little bit into JDK code, I finally find the reason. HttpURLConnection#getErrorStream() returns null when receiving a 401 or 407, not because the noop implementation in the abstract class, but because HttpURLConnection closes/clears the connection immediately if it sees a 401/407 when in streaming mode(i.e., POST). See the source of the concrete implementation of HttpURLConnection: http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/sun/net/www/protocol/http/HttpURLConnection.java#1079
That said, when you catch an IOException when calling getInputStream(), the connection to server is already closed and the underlining socket is cleared, so you would always get null when calling getErrorStream().
The other options many have suggested is to check the status code before calling getInputStream or getErrorStream. This won't for 401 and 407 either because the internal errorStream is only set when you call getInputStream, i.e., it's a basically a copy of the inputStream when status code != 200. But again when you call getInputStream, the connection will be closed.
Put the statement conn.setAllowUserInteraction(true);
before execute the request and the connection will not be closed, even receiving 401 status.
conn.setDoInput(true);
conn.setDoOutput(true);
conn.setAllowUserInteraction(true); // <<<--- ### HERE
//do something
conn.connect();
boolean isError = (conn.getResponseCode() >= 400);
InputSteam is = isError ? con.getErrorStream() : con.getInputStream();
As suggested in the Android documentation:
String responseString;
try {
responseString = readInputStream(con.getInputStream());
} catch (final IOException e) {
// This means that an error occurred, read the error from the ErrorStream
try {
responseString = readInputStream(con.getErrorStream());
} catch (IOException e1) {
throw new IllegalStateException("Unable to read error body.", e);
}
}
private String readInputStream(final InputStream inputStream) throws IOException {
final BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
final StringBuilder responseString = new StringBuilder();
String line;
while ((line = bufferedReader.readLine()) != null) {
responseString.append(line);
}
bufferedReader.close();
return responseString.toString();
}
精彩评论