我有以下代码:
HttpURLConnection conn = null;
BufferedReader in = null;
StringBuilder sb = null;
InputStream is = null;
conn = (HttpURLConnection) url.openConnection();
// Break-point A
conn.setDoInput(true);
conn.setDoOutput(true);
conn.setRequestMethod("POST");
// Break-point B
conn.setRequestProperty("X-TP-APP", Constants.X_TP_APP);
conn.setRequestProperty("X-TP-DEVICE", Constants.X_TP_DEVICE);
conn.setRequestProperty("X-TP-LOCALE", Constants.X_TP_LOCALE);
conn.setRequestProperty("Content-Type", contentType);
conn.setRequestProperty("Accept", accept);
conn.setRequestProperty("Authorization", SystemApi.TOKEN_STR);
conn.setUseCaches(false);
conn.setConnectTimeout(30000);
conn.getOutputStream().write(req.getBytes("UTF-8"));
conn.getOutputStream().flush();
conn.getOutputStream().close();
is = conn.getInputStream();
in = new BufferedReader(new InputStreamReader(is));
int statusCode = conn.getResponseCode();
// Break-point C
代码运行正常,没有问题(禁用断点(A,B)时)
我试图找出HttpURLConnection
何时真正调用该请求并将断点(A)放在conn = getConnection(strURL);
之后并继续执行代码,但是最后,在断点(C),服务器将返回401-未经授权,这意味着我的Authorization标头不在请求中。
似乎我们首先尝试打开一个连接,然后尽可能快地设置标题。如果我们不够快,那么无论如何都会调用该请求,这似乎不正确。
我的问题和关注:
HttpURLConnection
何时真正调用该请求?
这是实际发生的事情吗?这是正确的方法吗?
是否有更好的方法来确保在调用请求之前已设置标题?
根据文档,当在[connect()
] Http
上调用UrlConnection
方法时,才建立实际的连接。这可以手动完成,也可以通过某些其他方法隐式完成。 UrlConnection.connect()
的Javadocs部分说:
URLConnection对象经历两个阶段:首先创建它们,然后将它们连接起来。在创建之后和连接之前,可以指定各种选项(例如doInput和UseCaches)。连接后,尝试设置它们是错误的。如果需要,依赖于连接的操作(如getContentLength)将隐式执行连接。
特别注意最后一句话。在您的代码中,直到第一个conn.getOutputStream()
,我都看不到需要建立连接的任何内容,并且我读文档时说,连接对象只有在调用某种方法之后才会进入“已连接”状态那个需要那个。在此之前,可以设置连接属性。
此外,文档明确指出,如果在连接对象已经连接时调用设置连接属性(特别是setRequestProperty()
)的方法,则将抛出IllegalStateException
。
您的Java库可能以您描述的方式出现故障,但这肯定会与API规范冲突。我认为您所观察到的行为的解释更有可能是不同的,我建议您捕获并分析实际的HTTP流量以确定实际发生的情况。
实际上实际上发生的是,在调试模式下,我在表达式中使用了conn.getResponseCode()
,这迫使conn.getResponseCode()
运行。
尚未连接时,getResponseCode()
将在准备请求之前调用connect()
。
因此它将返回我401。
由于Android使用相同的HttpURLConnection
,所以我做了一些捕获数据包交换的操作,以了解幕后情况。
我在这篇文章Can you explain the HttpURLConnection connection process?中详细介绍了我的实验>
概述程序的网络活动。
在断点A
没有与远程服务器建立物理连接。您将获得本地连接对象的逻辑句柄。在断点B
您只需配置本地连接对象,仅此而已。conn.getOutputStream()
网络连接从此处开始,但是没有有效负载传输到服务器。conn.getInputStream()
有效负载(http标头,内容)被发送到服务器,您将获得响应(缓冲到输入流中,以及响应代码等)]回答您的问题
HttpURLConnection何时真正调用该请求?
getInputStream()触发网络层发送应用程序有效负载并获得响应。
这是实际发生的事情吗?这是正确的方法吗?
没有
openConnection()
不启动网络活动。您将获得本地句柄以用于将来的连接,而不是活动的连接。
是否有更好的方法来确保在调用请求之前已设置标题?
您无需确保已设置标题。在您请求响应(例如获取响应代码或打开inputStream)之前,标头有效负载不会发送到服务器。