1.Post請求失敗的代碼
try {
HttpResponse response = httpClient.execute(httpPost);
HttpEntity entity = response.getEntity();
result = EntityUtils.toString(entity, "UTF-8");
Util.log("API,POST回來的數據是:");
Util.log(result);
} catch (ConnectionPoolTimeoutException e) {
log.e("http get throw ConnectionPoolTimeoutException(wait time out)");
} catch (ConnectTimeoutException e) {
log.e("http get throw ConnectTimeoutException");
} catch (SocketTimeoutException e) {
log.e("http get throw SocketTimeoutException");
} catch (Exception e) {
log.e("http get throw Exception");
} finally {
httpPost.abort();
}
之前每次代碼執行到上述代碼的第二行的時候,會等一段時間然后會捕獲到Exception異常。
2.分析問題
當然捕獲的Exception這個異常太大了我們不便於分析,我們查看一下httpClient.execute(HttpUriRequest uri)的方法;

發下這個方法會拋出IOException, ClientProtocolException這兩個異常,但是在調用方法的時候並沒有明確捕獲他們兩個。
3.得出結論
所以很有可能在執行post請求的過程中,遇到了這兩個問題,果然我們把代碼完善之后
try {
httpClient = new SSLClient();
HttpResponse response = httpClient.execute(httpPost);
HttpEntity entity = response.getEntity();
result = EntityUtils.toString(entity, "UTF-8");
Util.log("API,POST回來的數據是:");
Util.log(result);
} catch (ConnectionPoolTimeoutException e) {
log.e("http get throw ConnectionPoolTimeoutException(wait time out)");
} catch (ConnectTimeoutException e) {
log.e("http get throw ConnectTimeoutException");
} catch (SocketTimeoutException e) {
log.e("http get throw SocketTimeoutException");
} catch (ClientProtocolException e) {
log.e("http get throw ClientProtocolException");
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
log.e("http get throw Exception");
} finally {
httpPost.abort();
}
上述,完善完畢代碼后捕捉到了IOException異常,我們把異常打印出來看到了如下信息。

4.解決問題
通過在網上查詢可知,這是缺少安全證書時出現的異常,解決方案如下:
- 等待Oracle/Google/Mozilla等等組織信任CNNIC,算了,洗洗睡吧
- 使用Java的TrustManager忽略所有的SSL請求的證書,僅僅用於開發測試,限於篇幅不做介紹了
- 導入目標網站的證書,然后在開始調用之前,指定keystore就ok了,本文介紹下該方法
目前我們采用第二種方案:由於請求的URL是HTTPS的,為了避免需要證書,所以用一個類繼承DefaultHttpClient類,忽略校驗過程。
編寫一個SSLClient類
package com.phicomm.smarthome.sharedwifi.util;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
public class SSLClient extends DefaultHttpClient {
public SSLClient() throws Exception {
super();
SSLContext ctx = SSLContext.getInstance("TLS");
X509TrustManager tm = new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
// TODO Auto-generated method stub
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
// TODO Auto-generated method stub
}
@Override
public X509Certificate[] getAcceptedIssuers() {
// TODO Auto-generated method stub
return null;
}
};
ctx.init(null, new TrustManager[] { tm }, null);
SSLSocketFactory ssf = new SSLSocketFactory(ctx, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
ClientConnectionManager ccm = this.getConnectionManager();
SchemeRegistry sr = ccm.getSchemeRegistry();
sr.register(new Scheme("https", 443, ssf));
}
}
對應的實現類:
public HttpResponse sendPostToService(String url, Object pushData) throws IOException, KeyStoreException,
UnrecoverableKeyException, NoSuchAlgorithmException, KeyManagementException {
if (!hasInit) {
init();
}
String result = null;
HttpPost httpPost = new HttpPost(url);
StringEntity postEntity = new StringEntity(pushData.toString(),
ContentType.create("application/x-www-form-urlencoded", "UTF-8"));
// 設置一些Http頭信息
httpPost.addHeader("Content-Type", "application/x-www-form-urlencoded");
httpPost.addHeader("connection", "Keep-Alive");
httpPost.addHeader("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
// 將發送內容填裝
httpPost.setEntity(postEntity);
// 設置請求器的配置
httpPost.setConfig(requestConfig);
// 打印待發送的數據
Util.log("=====API,POST過去的數據是:");
Util.log("executing request" + httpPost.getRequestLine());
Util.log("請求頭信息===" + httpPost.getAllHeaders().toString());
Util.log("請求狀態行===" + httpPost.getRequestLine());
Util.log("請求配置===" + httpPost.getConfig());
Util.log("請求實體===" + httpPost.getEntity().getContentEncoding() + httpPost.getEntity().getContentType()
+ httpPost.getEntity().getContent());
HttpResponse response = null;
try {
// 忽略所有的SSL請求的證書
httpClient = new SSLClient();
response = httpClient.execute(httpPost);
HttpEntity entity = response.getEntity();
result = EntityUtils.toString(entity, "UTF-8");
// 打印得到的響應信息
Util.log("API,POST回來的數據是:");
Util.log("=====Entity:" + result);
Util.log("=====Headers:" + response.getAllHeaders());
Util.log("=====StatusLine:" + response.getStatusLine());
Util.log("=====Locale:" + response.getLocale());
} catch (ConnectionPoolTimeoutException e) {
log.e("http get throw ConnectionPoolTimeoutException(wait time out)");
} catch (ConnectTimeoutException e) {
log.e("http get throw ConnectTimeoutException");
} catch (SocketTimeoutException e) {
log.e("http get throw SocketTimeoutException");
} catch (ClientProtocolException e) {
log.e("http get throw ClientProtocolException");
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
log.e("http get throw Exception");
} finally {
httpPost.abort();
}
return response;
}
在第36行使用自定義的SSLClient來忽略掉驗證要求
另外注意在postMan中模擬調用的時候我們是用的x-www-form-urlencoded格式的數據請求,就是application/x-www-from-urlencoded,會將表單內的數據轉換為鍵值對。
當action為get時候,瀏覽器用x-www-form-urlencoded的編碼方式把form數據轉換成一個字串(name1=value1&name2=value2...),然后把這個字串append到url后面,用?分割,加載這個新的url。 當action為post時候,瀏覽器把form數據封裝到http body中,然后發送到server。
所以我們需要對傳進來的數據做一下處理:
// 拼接x-www-form-urlencoded格式的請求參數
String www_url = "coverimg=" + pushMsgModel.getCoverimg() + "&mode=" + pushMsgModel.getMode() + "&msgcontent="
+ pushMsgModel.getMsgContent() + "&msgtype=" + pushMsgModel.getMsgtype() + "&outline="
+ pushMsgModel.getOutline() + "&saveRecord=" + pushMsgModel.getSaveRecord() + "&source="
+ pushMsgModel.getSource() + "&ticker=" + pushMsgModel.getTicker() + "×tamp="
+ pushMsgModel.getTimestamp() + "&title=" + pushMsgModel.getTitle() + "&uid=" + pushMsgModel.getUid()
+ "&url=" + pushMsgModel.getUrl();
logger.info("x-www-form-urlencoded格式的請求參數為:" + www_url);
最后效果如下:

