Android 出现Http 302跳转问题
最近在做一个网络诊断功能,检测外网连通性,测试地址是http://www.baidu.com。
问题出现
测试方法:
HttpURLConnection进行网络连接访问,根据访问返回的http状态码是否为200,确定外网是否连通。测试代码:
int resCode = -1;
try {
URL url = new URL(urlStr);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
//信任https站点
if (conn instanceof HttpsURLConnection) {
HttpsURLConnection conns = (HttpsURLConnection) conn;
conns.setHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
SSLContext sslCtx = SSLContext.getInstance("SSL");
sslCtx.init(null, new TrustManager[]{new XulWorker.acceptAllTrustManager()}, new SecureRandom());
if (sslCtx != null) {
conns.setSSLSocketFactory(sslCtx.getSocketFactory());
}
}
conn.setRequestMethod("GET");
conn.setConnectTimeout(timeoutMillis);
conn.connect();
resCode = conn.getResponseCode(); // 获取get请求responseCode
} catch (Exception e) {
e.printStackTrace();
} finally {
if (callback != null) {
callback.compareTo((resCode == 200 ? 0 : -1) == 0);
}
}
测试结果:
大部分设备上该方式处理没有问题,在极少的几个设备上,发现永远是非200的状态码。但是本身也是可以连接外网的,访问没有问题。
分析:
通过打印状态码,发现状态码为302错误。那302错误大家都应该很熟悉了,就是访问地址被移出到另外的地址了,需要重新访问另外的地址。
举个例子:
在浏览器输入http://www.sogou.com,进行抓包或者通过浏览器F12查看network访问,如下图,
访问http://www.sogou.com后,服务器吐回状态码302 ,然后Locatioin字段标识浏览器应该跳转到了 。
那之前的代码健壮性不强,没有考虑到302跳转场景,怎么来规避呢?
解决办法
开始时候,想的是使用简单的方式达到效果,翻阅了的HttpsURLConnection 类的所有方法,存在这个方法:
/**
* Sets whether this connection follows redirects.
*
* @param followRedirects
* {@code true} if this connection will follows redirects, false
* otherwise.
*/
public void setInstanceFollowRedirects(boolean followRedirects) {
instanceFollowRedirects = followRedirects;
}
从这个方法来看,貌似是可以设置是否支持重定向访问,所以这样设置:
conn.setInstanceFollowRedirects(true);
结果,访问302的地址还是失败,根本不会自动跳转访问。也不清楚具体原因,估计要看源码了,所以换了方式处理该问题;
方式1: 解析http请求返回的Location,进行重新访问请求
示例代码:
private static int reload(String urlStr, int timeoutMillis) throws Exception {
URL url = new URL(urlStr);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
//信任https站点
if (conn instanceof HttpsURLConnection) {
HttpsURLConnection conns = (HttpsURLConnection) conn;
conns.setHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
SSLContext sslCtx = SSLContext.getInstance("SSL");
sslCtx.init(null, new TrustManager[]{new XulWorker.acceptAllTrustManager()}, new SecureRandom());
if (sslCtx != null) {
conns.setSSLSocketFactory(sslCtx.getSocketFactory());
}
}
conn.setRequestMethod("GET");
conn.setConnectTimeout(timeoutMillis);
conn.connect();
if (huc.getResponseCode() == HttpURLConnection.HTTP_MOVED_TEMP
|| huc.getResponseCode() == HttpURLConnection.HTTP_MOVED_PERM)// 302, 301
return reload(huc.getHeaderField("location"), timeoutMillis);
return huc.getResponseCode();
}
方式2:使用AndroidHttpClient进行网络访问
通过设置ClientPNames.HANDLE_REDIRECTS为true,来达到重定向请求功能。
ps:DefaultHttpClient是默认支持http get的重定向访问,也可以直接用该类实现请求。post还是不支持的。
public static int getResponseCode(String urlStr, int timeoutMillis) {
int resCode = -1;
try {
AndroidHttpClient httpClient = AndroidHttpClient.newInstance("Network check");
//兼容服务器端重定向url
HttpParams params = httpClient.getParams();
params.setParameter(ClientPNames.HANDLE_REDIRECTS, true);
HttpConnectionParams.setSoTimeout(params, timeoutMillis);
HttpGet get_url = new HttpGet(urlStr);
HttpResponse response = httpClient.execute(get_url);
StatusLine statusLine = response.getStatusLine();
resCode = statusLine.getStatusCode();
Logger.d(TAG, "isNetWorkAvailableOfGet() called resCode: "+resCode +"," + urlStr );
} catch (Exception e) {
Logger.d(TAG, "isNetWorkAvailableOfGet() Exception: "+ e.getMessage() +"," + urlStr);
e.printStackTrace();
} finally {
return resCode;
}
}
结论
302跳转本身是比较常见的一种访问访问结果,在写网络请求时候,需要考虑,并且选择合适的方式来处理,增强代码健壮性。