如何在HttpURLConnection返回非2xx状态码时获取响应正文?

135

当服务器返回错误时,我在检索Json响应方面存在问题。请参见下面的详细信息。

如何执行请求

我使用 java.net.HttpURLConnection。 我设置请求属性,然后执行以下操作:

conn = (HttpURLConnection) url.openConnection();

之后,当请求成功时,我会收到响应的 JSON 数据:

br = new BufferedReader(new InputStreamReader((conn.getInputStream())));
sb = new StringBuilder();
String output;
while ((output = br.readLine()) != null) {
  sb.append(output);
}
return sb.toString();

...问题是:

当服务器返回50x或40x等一些错误时,我无法检索到接收到的Json。以下行会抛出IOException异常:

br = new BufferedReader(new InputStreamReader((conn.getInputStream())));
// throws java.io.IOException: Server returned HTTP response code: 401 for URL: www.example.com
服务器肯定会发送正文内容,我在外部工具Burp Suite中已经看到了。
HTTP/1.1 401 Unauthorized

{"type":"AuthApiException","message":"AuthApiException","errors":[{"field":"email","message":"Invalid username and/or password."}]}

我可以使用以下方法获取响应消息(例如,“Internal Server Error”)和代码(例如,“500”):

conn.getResponseMessage();
conn.getResponseCode();

但是我无法获取请求正文...也许在库中有一些我没有注意到的方法吗?

3个回答

156
如果响应代码不是 200 或者 2xx 的话,请使用 getErrorStream() 而不是 getInputStream()

4
在我的情况下,当响应代码为403时,调用 getErrorStream() 返回null。 - Sip
2
@Sip 403是“禁止”的意思。响应有效载荷是可选的,但不是必需的。https://datatracker.ietf.org/doc/html/rfc7231#section-6.5.3 - The incredible Jan

108

使用了错误的方法处理错误,这里是可用的代码:

BufferedReader br = null;
if (100 <= conn.getResponseCode() && conn.getResponseCode() <= 399) {
    br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
} else {
    br = new BufferedReader(new InputStreamReader(conn.getErrorStream()));
}

3
HttpURLConnection.getErrorStream()sun.net.www.protocol.http 实现)仅在 responseCode >= 400 时返回 null,因此您对于 299 的检查可能是不正确的。 - vladr
15
如果您使用的是Java 8,您可以将响应作为字符串获取。 String responseBody = br.lines().collect(Collectors.joining()); - Lanil Marasinghe
1
正确的范围是200...399,因为3xx是“重定向”状态,而不是错误。 - luca.vercelli
1
@luca.vercelli 我认为应该是100..399,因为100..199用于“信息性”响应,如https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#1xx_informational_response中所定义。 - WindRider

14

这是一种轻松的方法,可以从服务器获得成功响应,就像 PHP echo 一样,否则会返回错误消息。

BufferedReader br = null;
if (conn.getResponseCode() == 200) {
    br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
    String strCurrentLine;
        while ((strCurrentLine = br.readLine()) != null) {
               System.out.println(strCurrentLine);
        }
} else {
    br = new BufferedReader(new InputStreamReader(conn.getErrorStream()));
    String strCurrentLine;
        while ((strCurrentLine = br.readLine()) != null) {
               System.out.println(strCurrentLine);
        }
}

1
if(...) 应该检查更多成功的代码,实际上是所有2xx,而不仅仅是200。 - kiedysktos

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接