1 在实际的开发中,如果要获取后台值,我们会用增量或者轻量级的服务,我们来看看在携程中,http 服务的使用方式
具体的场景是在播放器上,现实一段的文案,通过http 服务的方式去请求:
具体代码是:
public static class GetHotelVideoInfo extends BaseHTTPRequest {
private int HotelId;
private int HotelStar;
private int HotelDataType;
public GetHotelVideoInfo(int hotelId, int hotelStar, boolean isOverSea) {
this.HotelId = hotelId;
this.HotelStar = hotelStar;
this.HotelDataType = isOverSea ? 2 : 1;
}
@Override
public String getPath() {
return "";
}
@Override
public String getUrl() {
return Env.isFAT() ? TESTEVN_URL : PRODUCT_URL;
}
}
private void sendPlayCountService() {
if (mHotelVideo == null) {
return;
}
if (mHotelId <= 0) {
return;
}
SOAHTTPHelperV2.getInstance().sendRequest(new GetHotelVideoInfo(mHotelId, mHotelStarLevel, mIsOverSea), new SOAHTTPHelperV2.HttpCallback<com.alibaba.fastjson.JSONObject>() {
@Override
public void onFailed(BaseHTTPRequest requst, Exception e) {
}
@Override
public void onSuccess(com.alibaba.fastjson.JSONObject jsonObject) {
if (jsonObject == null) {
return;
}
mPlayCountText = jsonObject.getString("PlayText");
}
});
}
可以看出,我们是为了获取playText 这个字断的值,我们的request 是继承了 BaseHTTPRequest,这个类中初始化了一些必备的参数,我们看看有哪些
public enum Method {
GET, POST, MULTIPART_POST
}
// 请求方法
@ParamsIgnore
private Method method = Method.POST;
// 默认序列化编码
@ParamsIgnore
private String encoding = "utf-8";
// 超时时间 默认10*1000
@ParamsIgnore
private int timeout = 30 * 1000;
// 协议 默认https
@ParamsIgnore
private boolean https = true;
/ /默认的请求 head 字段
protected JSONObject head = CtripHTTPClientV2.buildRequestHeadForFastjson(null);
@ParamsIgnore
private Map<String, Object> params;
@ParamsIgnore
String fullUrl = "";
@ParamsIgnore
private boolean isRetry = false;
我们注意到其中有一段代码,
// 默认的请求 head 字段
protected JSONObject head = CtripHTTPClientV2.buildRequestHeadForFastjson(null);
我们看看其中的具体实现方式:
@Override
public JSONObject getFastJsonHead(Map<String, Object> extension) {
try {
SharedPreferences sharedPreferences = PreferenceManager
.getDefaultSharedPreferences(FoundationContextHolder
.context);
// 添加head
com.alibaba.fastjson.JSONObject headObject = new com.alibaba.fastjson.JSONObject();
headObject.put("syscode", CtripConfig.SYSTEMCODE);
headObject.put("lang", CtripConfig.LANGUAGE);
headObject.put("auth", CtripLoginManager.getLoginTicket());
headObject.put("cid", ClientID.getClientID());
headObject.put("ctok", DeviceInfoUtil.getAndroidID());
headObject.put("cver", CtripConfig.VERSION);
headObject.put("sid", CtripConfig.SOURCEID);
headObject.put("sauth", sharedPreferences.getString("sauth", ""));
if (extension != null && extension.keySet().size() > 0) {
com.alibaba.fastjson.JSONArray extensionArray = new com.alibaba.fastjson.JSONArray();
for (String key : extension.keySet()) {
JSONObject ext = new JSONObject();
ext.put("name", key);
ext.put("value", extension.get(key));
extensionArray.add(ext);
}
headObject.put("extension", extensionArray);
}
return headObject;
} catch (Exception e) {
e.printStackTrace();
return new JSONObject();
}
}
我们看到这里其实是网络请求中,要带的一些设备,用户等相关信息,我们接着朝下看:
/**
* 发送Http 请求
* @param request http请求,继承自BaseRequest
* @param callback 请求回调
*
* **/
public <T extends BaseHTTPRequest> String sendRequest(final T request, final HttpCallback<JSONObject> callback) {
try {
if (request == null) {
invokeFailedCallback(callback, request, new NullPointerException("request is null!"));
return null;
}
return doRequest(request, JSONObject.class, callback);
} catch (Exception e) {
invokeFailedCallback(callback, request, e);
}
return null;
}
其中核心的就是
doRequest(request, JSONObject.class, callback);
我们接着看这里具体是做什么的:
/**
* 发送Http 请求具体实现
* @param request http请求,继承自BaseRequest
* @param responseClass http api 返回对象
* @param callback 请求回调
*
* **/
private <T extends BaseHTTPRequest, V> String doRequest(final T request, final Class<V> responseClass, final HttpCallback<V> callback) throws NoSuchAlgorithmException, KeyManagementException {
final long startTimestamp = System.currentTimeMillis();
CtripHTTPCallbackV2 ctripHTTPCallback = new CtripHTTPCallbackV2() {
@Override
public void onFailure(CtripHttpFailure failure) {
long costTime=System.currentTimeMillis()-startTimestamp;
if(failure!=null
&& failure.getException()!=null
&& TCP_CONNECTION_FAIL.equals(failure.getException().getMessage())
&& costTime < request.getTimeout()){
try {
String url = generateUrl(request);
if(HttpConfig.getHttpConfig().httpRequestInterceptor!=null && HttpConfig.getHttpConfig().httpRequestInterceptor.needInterceptSOARequest(url,"")) {
int retryTimeout=(int)(request.getTimeout()-costTime);
LogUtil.d("http do request retry retryTimeout="+retryTimeout);
request.setRetry(true);
request.setTimeout(retryTimeout < kMinTimeout ? kMinTimeout : retryTimeout);
doRequest(request, responseClass, callback);
LogUtil.d("http do request retry");
}
}catch (Exception e){
LogUtil.e("retry error:"+e.getMessage());
}
return;
}
invokeFailedCallback(callback, request, failure.getException());
}
@Override
public void onResponse(CtripHttpResponse response) {
if (callback != null) {
try {
V v = null;
if (responseClass != null) {
v = parseResponse(response.getResponse());
}
// 解析ResponseStatus
if (isAckFailed(v)) {
invokeFailedCallback(callback, request, new SOAACKException("soa http ACK is failed"));
return;
}
invokeSuccessCallback(callback, v);
} catch (Exception e) {
invokeFailedCallback(callback, request, e);
}
}
}
private V parseResponse(Response response) throws IOException {
String responseStr = new String(response.body().bytes(), request.getEncoding());
LogUtil.d("SOAHTTPHelper response:" + response.request().url().toString() + ", " + responseStr);
if (responseClass == JSONObject.class) {
return (V) JSON.parseObject(responseStr);
} else {
return JsonUtils.parse(responseStr, responseClass);
}
}
};
String requestTag=null;
// 生成URL
String url = generateUrl(request);
// 非pro环境 打出请求log
if (HttpConfig.getHttpConfig().canShowLog()) {
LogUtil.d("SOAHTTPHelper request:" + url + "," + JsonUtils.toJson(request.getParams()));
}
LogUtil.d("SOAHTTPHelper request:" + url + ",isRetry=" + request.isRetry());
String bodyData=JsonUtils.toJson(request.getParams());
if(!request.isRetry() && HttpConfig.getHttpConfig().httpRequestInterceptor!=null
&& HttpConfig.getHttpConfig().httpRequestInterceptor.needInterceptSOARequest(url,bodyData)){
requestTag = HttpConfig.getHttpConfig().httpRequestInterceptor.interceptSOARequest(
url,
request.getMethod().name(),
HttpConfig.getHttpConfig().httpHeaderFactory.getFastJsonHead(null),
bodyData,
System.currentTimeMillis()+"",
request.getTimeout(),
"Native SOA",ctripHTTPCallback);
return url+":"+requestTag;
}
switch (request.getMethod()) {
case POST:
requestTag = httpClient.asyncPost(url, request.getParams(), ctripHTTPCallback, request.getTimeout(), HttpConfig.getHttpConfig().httpHeaderFactory.getHttpHeaders());
break;
case GET:
requestTag = httpClient.asyncGet(url, request.getParams(), ctripHTTPCallback, request.getTimeout(), HttpConfig.getHttpConfig().httpHeaderFactory.getHttpHeaders());
break;
case MULTIPART_POST:
LogUtil.e("multipart post not supported yet!");
// not supported yet;
break;
}
return requestTag;
}
这段代码比较长,上面是网络请求成功后的回调,我们从
String requestTag=null;
// 生成URL
String url = generateUrl(request);
我们来看下 这段 generateUrl 的实现方式为:
@Override
public String getBaseUrl(String pathUrl, boolean isHttps) {
String URL_FAT_PREFIX = "gateway.m.fws.qa.nt.ctripcorp.com/restapi/soa2/";
String URL_UAT_PREFIX = "gateway.m.uat.qa.nt.ctripcorp.com/restapi/soa2/";
String URL_PRO_PREFIX = "m.ctrip.com/restapi/soa2/";
String URL_PRO_PREFIX_HTTPS = "m.ctrip.com/restapi/soa2/";
StringBuilder urlBuilder = new StringBuilder();
urlBuilder.append(isHttps ? "https://" : "http://");
if (Env.isFAT()) {
urlBuilder.append(URL_FAT_PREFIX);
} else if (Env.isUAT()) {
urlBuilder.append(URL_UAT_PREFIX);
} else {
urlBuilder.append(isHttps ? URL_PRO_PREFIX_HTTPS : URL_PRO_PREFIX);
}
return urlBuilder.toString();
}
我们接着朝下看:
String bodyData=JsonUtils.toJson(request.getParams());
这个String 类型的格式:
我们看到bodyData 中的数据 获取的方式是:
// 通过反射获取所有字段键值对
public Map<String, Object> getParams() {
Map<String, Object> params = this.params;
if (params == null) {
params = new HashMap<>();
}
for(Class<?> clazz = this.getClass(); clazz != Object.class ; clazz = clazz.getSuperclass()) {
Field[] fileds = clazz.getDeclaredFields();
for(Field field : fileds) {
// 过滤注解忽略的字段
if (field.isAnnotationPresent(ParamsIgnore.class)) {
continue;
}
//过滤匿名内部类持有的外部类字段
if(field.getName().contains("this$") && "1010".equals(Integer.toHexString(field.getModifiers()))){
continue;
}
field.setAccessible(true);
try {
if(params.containsKey("head") && "head".equals(field.getName())){
continue;
}
params.put(field.getName(), field.get(this));
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
return params;
}
通过分析我们看到,bodyData 中的数据就是通过反射的方式,获取到resquest 这个类中的所有字段
我们接着朝下看:看到这段代码是:
if(!request.isRetry() && HttpConfig.getHttpConfig().httpRequestInterceptor!=null
&& HttpConfig.getHttpConfig().httpRequestInterceptor.needInterceptSOARequest(url,bodyData)){
requestTag = HttpConfig.getHttpConfig().httpRequestInterceptor.interceptSOARequest(
url,
request.getMethod().name(),
HttpConfig.getHttpConfig().httpHeaderFactory.getFastJsonHead(null),
bodyData,
System.currentTimeMillis()+"",
request.getTimeout(),
"Native SOA",ctripHTTPCallback);
return url+":"+requestTag;
}
这里有判断条件的,默认条件下 !request.isRetry() 是为true 的,我们看关键的一段代码是:
HttpConfig.getHttpConfig().httpRequestInterceptor.needInterceptSOARequest(url,bodyData)
我们看看这段代码中的实现方式:
@Override
public boolean needInterceptSOARequest(String url,String bodyData) {
long bodySize = 0;
try {
if(!StringUtil.isEmpty(bodyData))
bodySize = bodyData.getBytes().length * 8;
} catch (Exception e) {
LogUtil.e("erorr when cacl body size");
}
if(bodySize > CtripAppHttpSotpManager.TCP_HTTP_MAX_BODY_SIZE)
LogUtil.d("needInterceptSOARequest","bodySize > CtripAppHttpSotpManager.TCP_HTTP_MAX_BODY_SIZE");
return CtripSOTPConfig.needHttpToTcpForSoa(url)
&& !CtripSOTPConfig.isUrlInLocalBlackList(url)//本地黑名单
&& bodySize < CtripAppHttpSotpManager.TCP_HTTP_MAX_BODY_SIZE;//请求体小于最大限制
}
我们看到这里,看到这里有 bodySize 的控制大小,关键看renturn 中的判断条件,
我们来看看
CtripSOTPConfig.needHttpToTcpForSoa(url)
看看它的具体的实现方式:
public static boolean needHttpToTcpForSoa(String url) {
if (TextUtils.isEmpty(url)) {
return false;
}
try {
Uri urlFull = Uri.parse(url);
if (isHttpToTcpEnabledForSoaWithLocal()
&& !isUrlInNativeSOTPBlackList(url)//服务端下发的黑名单
&& "m.ctrip.com".equals(urlFull.getHost())
&& urlFull.getPath().startsWith("/restapi/soa2")) {
return true;
}
} catch (Exception e) {
LogUtil.e("error when parse http tp tcp url", e);
return false;
}
return false;
}
可以看出来,这里是类似对些特定的服务地址处理的,对于我举的例子中的url 的格式为:
"https://m.ctrip.com/restapi/soa2/12465/h5-json/getHotelVideoPlayInfo";
我们可以看到这个url 在needHttpToTcpForSoa 这个方法函数,通常我们请求是小于,请求bodysize 的,
所以我们走到了判断条件的方法体中,
HttpConfig.getHttpConfig().httpRequestInterceptor.interceptSOARequest(
url,
request.getMethod().name(),
HttpConfig.getHttpConfig().httpHeaderFactory.getFastJsonHead(null),
bodyData,
System.currentTimeMillis()+"",
request.getTimeout(),
"Native SOA",ctripHTTPCallback);
我们看看这里的实现方式:
final long startTimestamp = System.currentTimeMillis();
return CtripAppHttpSotpManager.sendAppHttpRequestForSOA(url, method, headerJson, bodyData, sequenceId, timeout, fromCode, new CtripAppHttpSotpManager.NetworkRequestCallbackListener() {
@Override
public void onCallBack(boolean success, String errorCode, TaskFailEnum failEnum, JSONObject responseJson) {
MediaType mediaType = MediaType.parse("application/json; charset=utf-8");
RequestBody requestBody=RequestBody.create(mediaType,bodyData);
//构造Request
Request.Builder requestBuilder = new Request.Builder();
requestBuilder.url(url);
requestBuilder.method(method,requestBody);
requestBuilder.tag("useSotp");
Request request=requestBuilder.build();
if (success) {
//构造Response
Response.Builder responseBuilder = new Response.Builder();
responseBuilder.request(request);
responseBuilder.body(ResponseBody.create(mediaType, responseJson.getString("body")));
responseBuilder.protocol(Protocol.HTTP_1_1);
responseBuilder.code(200);
Response response=responseBuilder.build();
CtripHttpResponse ctripHttpResponse = new CtripHttpResponse();
ctripHttpResponse.setResponse(response);
try {
LogUtil.d("interceptSOARequest", "interceptSOARequest success,callback.onResponse--"+url);
ctripHTTPCallbackV2.onResponse(ctripHttpResponse);
CtripHTTPClientV2.getInstance().logHTTPRequestMetrics(request,response,"",startTimestamp,false);
} catch (Exception e) {
LogUtil.d("interceptSOARequest", "interceptSOARequest exception:" + e.getMessage());
}
} else {
LogUtil.d("interceptSOARequest errorCode="+errorCode);
//203错误才走http重试
if (failEnum == TaskFailEnum.CONNECTION_FAIL) {
CtripHttpFailure ctripHttpFailure = new CtripHttpFailure();
ctripHttpFailure.setException(new IOException(SOAHTTPHelperV2.TCP_CONNECTION_FAIL));
ctripHTTPCallbackV2.onFailure(ctripHttpFailure);
} else if (StringUtil.equals(errorCode,CtripHTTPClientV2.RESPONSE_CODE_431+"")
|| StringUtil.equals(errorCode,CtripHTTPClientV2.RESPONSE_CODE_432+"")
|| StringUtil.equals(errorCode,CtripHTTPClientV2.RESPONSE_CODE_429+"")
|| StringUtil.equals(errorCode,CtripHTTPClientV2.RESPONSE_CODE_430+"")){
antiBot(errorCode);
}else {
CtripHttpFailure ctripHttpFailure = new CtripHttpFailure();
ctripHttpFailure.setException(new IOException(errorCode + ""));
ctripHTTPCallbackV2.onFailure(ctripHttpFailure);
LogUtil.d("interceptSOARequest", "interceptSOARequest fail,callback.onFailure--" + url);
errorCode=errorCode+";"+Task.getFailCode(failEnum);
CtripHTTPClientV2.getInstance().logHTTPRequestMetrics(request, null, errorCode, startTimestamp, false);
}
LogUtil.d("interceptSOARequest","errorCode="+errorCode);
}
}
});
}
好长的代码,我们看下,代码中的实现方式:
sendAppHttpRequestForSOA
的实现方式为:
public static String sendAppHttpRequestForSOA(String url, String method, JSONObject headerJson, String bodyData, final String sequenceId, long timeout, final String fromCode, final CtripAppHttpSotpManager.NetworkRequestCallbackListener listener) {
final long requestStartTime = System.currentTimeMillis();
CtripAppHttpRequest request = new CtripAppHttpRequest();
if(url == null) {
url = "";
}
if(headerJson == null) {
headerJson = new JSONObject();
}
if(StringUtil.equalsIgnoreCase(method, "get")) {
method = "GET";
} else {
method = "POST";
}
if(!headerJson.containsKey("User-Agent")) {
headerJson.put("User-Agent", AppInfoConfig.getAppUserAgent());
}
if(!headerJson.containsKey("Accept")) {
headerJson.put("Accept", "application/json");
}
if(!headerJson.containsKey("Content-Type")) {
headerJson.put("Content-Type", "application/json;charset=utf-8");
}
if(bodyData == null) {
bodyData = "";
}
final String requestUrl = url.trim();
JSONObject bodyJson = new JSONObject();
bodyJson.put("headers", headerJson.toString());
bodyJson.put("cookies", CookieManager.getInstance().getCookie(requestUrl));
bodyJson.put("url", requestUrl);
bodyJson.put("method", method.toUpperCase());
bodyJson.put("body", bodyData);
request.body = bodyJson.toString();
LogUtil.d("CtripAppHttpSotpManager", "请求报文:" + request.body);
SenderResultModel resultModel = sendAppHttpRequest(request, new CtripAppHttpSotpManager.CallBack() {
public void onAppHttpRequestResult(BusinessResponseEntity responseEntity, int error) {
double elapseTime = (double)(System.currentTimeMillis() - requestStartTime) / 1000.0D;
HashMap<String, String> tagMap = new HashMap();
tagMap.put("url", requestUrl);
tagMap.put("log_from", fromCode);
JSONObject result = new JSONObject();
Pair codePairx;
if(error != 0) {
result.put("sequenceId", sequenceId);
String errorCode = String.valueOf(error);
tagMap.put("error_code", responseEntity == null?"1":Task.getFailCode(responseEntity.getFailType()));
codePairx = CtripHTTPClientV2.getInstance().parseSOACode(requestUrl);
if(codePairx != null) {
tagMap.put("serviceCode", codePairx.first);
tagMap.put("operation", codePairx.second);
} else {
tagMap.put("serviceCode", "");
tagMap.put("operation", "");
}
tagMap.put("cookies", CookieManager.getInstance().getCookie(requestUrl));
LogUtil.logMetrics("o_sotp_send_http_request_fail", Double.valueOf(elapseTime), tagMap);
TaskFailEnum taskFailEnum = TaskFailEnum.NO_FAIL;
if(responseEntity != null) {
taskFailEnum = responseEntity.getFailType();
} else if(error == 90003) {
taskFailEnum = TaskFailEnum.GET_CONNECTION_FAIL;
}
listener.onCallBack(false, errorCode, taskFailEnum, result);
LogUtil.d("CtripAppHttpSotpManager", "请求失败:" + result.toString() + "," + errorCode);
} else {
try {
CtripAppHttpResponse response = (CtripAppHttpResponse)responseEntity.getResponseBean();
org.json.JSONObject jsonObject = new org.json.JSONObject(response.body);
String header = jsonObject.optString("headers", "");
org.json.JSONObject headerJson = new org.json.JSONObject(header);
String status = headerJson.optString("Status", "1");
String cookies = jsonObject.optString("cookies", "");
String url = jsonObject.optString("url", "");
String body = jsonObject.optString("body", "");
CookieManager.getInstance().setCookie(requestUrl, cookies);
tagMap.put("http_status", status);
String traceId = headerJson.optString("CLOGGING_TRACE_ID", "");
String messageId = headerJson.optString("RootMessageId", "");
if(!TextUtils.isEmpty(traceId)) {
tagMap.put("CLOGGING_TRACE_ID", traceId);
}
if(!TextUtils.isEmpty(messageId)) {
tagMap.put("RootMessageId", messageId);
}
tagMap.put("http_status", status);
Pair codePair;
if(status.equals("200")) {
result.put("sequenceId", sequenceId);
result.put("status", status);
result.put("response_header", header);
result.put("body", body);
listener.onCallBack(true, "", responseEntity.getFailType(), result);
codePair = CtripHTTPClientV2.getInstance().parseSOACode(requestUrl);
if(codePair != null) {
tagMap.put("serviceCode", codePair.first);
tagMap.put("operation", codePair.second);
} else {
tagMap.put("serviceCode", "");
tagMap.put("operation", "");
}
tagMap.put("cookies", CookieManager.getInstance().getCookie(requestUrl));
LogUtil.logMetrics("o_sotp_send_http_request_success", Double.valueOf(elapseTime), tagMap);
LogUtil.d("CtripAppHttpSotpManager", "请求成功-返回报文" + result.toString());
} else {
result.put("sequenceId", sequenceId);
tagMap.put("error_code", responseEntity == null?"1":Task.getFailCode(responseEntity.getFailType()));
codePair = CtripHTTPClientV2.getInstance().parseSOACode(requestUrl);
if(codePair != null) {
tagMap.put("serviceCode", codePair.first);
tagMap.put("operation", codePair.second);
} else {
tagMap.put("serviceCode", "");
tagMap.put("operation", "");
}
tagMap.put("cookies", CookieManager.getInstance().getCookie(requestUrl));
LogUtil.logMetrics("o_sotp_send_http_request_fail", Double.valueOf(elapseTime), tagMap);
listener.onCallBack(false, status, responseEntity.getFailType(), result);
LogUtil.d("CtripAppHttpSotpManager", "请求失败:" + result.toString());
}
} catch (Exception var18) {
var18.printStackTrace();
result.put("sequenceId", sequenceId);
tagMap.put("error_code", "-30001");
tagMap.put("errorDescription", "parse response error:" + var18.getMessage());
codePairx = CtripHTTPClientV2.getInstance().parseSOACode(requestUrl);
if(codePairx != null) {
tagMap.put("serviceCode", codePairx.first);
tagMap.put("operation", codePairx.second);
} else {
tagMap.put("serviceCode", "");
tagMap.put("operation", "");
}
tagMap.put("cookies", CookieManager.getInstance().getCookie(requestUrl));
LogUtil.logMetrics("o_sotp_send_http_request_fail", Double.valueOf(elapseTime), tagMap);
listener.onCallBack(false, "-30001", responseEntity.getFailType(), result);
LogUtil.d("CtripAppHttpSotpManager", "请求失败:" + result.toString() + "," + "-30001");
}
}
}
}, timeout);
return resultModel.getToken();
其实这段的代码实现两个功能,一是用sotp 服务发送服务,它的序列化放式用的json ,二是处理返回的结果:
我们先看sotp 服务发送的服务,我们看代码
sendAppHttpRequest
的实现方式:
public static SenderResultModel sendAppHttpRequest(CtripAppHttpRequest request, final CtripAppHttpSotpManager.CallBack callback, long timeout) {
final SenderResultModel resultModel = Sender.createSenderResult("CtripAppHttpRequest");
List var5 = tokenList;
synchronized(tokenList) {
tokenList.add(resultModel.getToken());
}
if(timeout < 5000L || timeout > 120000L) {
timeout = 30000L;
}
BusinessRequestEntity requestEntity = BusinessRequestEntity.getInstance();
requestEntity.setRequestBean(request);
requestEntity.setShortConn(true);
requestEntity.setProtocolBuffer(false);
Sender.senderService(resultModel, callBack, new BusinessRequestEntity[]{requestEntity});
return resultModel;
}
我们看到这段时中下面的几行代码,是用sotp 的短连接的方式实现网络请求,这个我在上篇的博客
https://blog.csdn.net/ahubenkui/article/details/81417386
中写道过,走的携程自己封装的SOTP 服务,
我们再看看后面的网络回来的数据,
if(status.equals("200")) {
result.put("sequenceId", sequenceId);
result.put("status", status);
result.put("response_header", header);
result.put("body", body);
listener.onCallBack(true, "", responseEntity.getFailType(), result);
我们看到返回的 关键字段通过回调通过 result 这个值返回,
我们再看之前的代码,
@Override
public void onCallBack(boolean success, String errorCode, TaskFailEnum failEnum, JSONObject responseJson) {
MediaType mediaType = MediaType.parse("application/json; charset=utf-8");
RequestBody requestBody=RequestBody.create(mediaType,bodyData);
//构造Request
Request.Builder requestBuilder = new Request.Builder();
requestBuilder.url(url);
requestBuilder.method(method,requestBody);
requestBuilder.tag("useSotp");
Request request=requestBuilder.build();
if (success) {
//构造Response
Response.Builder responseBuilder = new Response.Builder();
responseBuilder.request(request);
responseBuilder.body(ResponseBody.create(mediaType, responseJson.getString("body")));
responseBuilder.protocol(Protocol.HTTP_1_1);
responseBuilder.code(200);
Response response=responseBuilder.build();
CtripHttpResponse ctripHttpResponse = new CtripHttpResponse();
ctripHttpResponse.setResponse(response);
try {
LogUtil.d("interceptSOARequest", "interceptSOARequest success,callback.onResponse--"+url);
ctripHTTPCallbackV2.onResponse(ctripHttpResponse);
CtripHTTPClientV2.getInstance().logHTTPRequestMetrics(request,response,"",startTimestamp,false);
} catch (Exception e) {
LogUtil.d("interceptSOARequest", "interceptSOARequest exception:" + e.getMessage());
}
} else {
LogUtil.d("interceptSOARequest errorCode="+errorCode);
//203错误才走http重试
if (failEnum == TaskFailEnum.CONNECTION_FAIL) {
CtripHttpFailure ctripHttpFailure = new CtripHttpFailure();
ctripHttpFailure.setException(new IOException(SOAHTTPHelperV2.TCP_CONNECTION_FAIL));
ctripHTTPCallbackV2.onFailure(ctripHttpFailure);
} else if (StringUtil.equals(errorCode,CtripHTTPClientV2.RESPONSE_CODE_431+"")
|| StringUtil.equals(errorCode,CtripHTTPClientV2.RESPONSE_CODE_432+"")
|| StringUtil.equals(errorCode,CtripHTTPClientV2.RESPONSE_CODE_429+"")
|| StringUtil.equals(errorCode,CtripHTTPClientV2.RESPONSE_CODE_430+"")){
antiBot(errorCode);
}else {
CtripHttpFailure ctripHttpFailure = new CtripHttpFailure();
ctripHttpFailure.setException(new IOException(errorCode + ""));
ctripHTTPCallbackV2.onFailure(ctripHttpFailure);
LogUtil.d("interceptSOARequest", "interceptSOARequest fail,callback.onFailure--" + url);
errorCode=errorCode+";"+Task.getFailCode(failEnum);
CtripHTTPClientV2.getInstance().logHTTPRequestMetrics(request, null, errorCode, startTimestamp, false);
}
LogUtil.d("interceptSOARequest","errorCode="+errorCode);
}
}
});
}
我们看到这里是用的okhttp 进行包装,包括code,等信息。
我们接着分析:
switch (request.getMethod()) {
case POST:
requestTag = httpClient.asyncPost(url, request.getParams(), ctripHTTPCallback, request.getTimeout(), HttpConfig.getHttpConfig().httpHeaderFactory.getHttpHeaders());
break;
case GET:
requestTag = httpClient.asyncGet(url, request.getParams(), ctripHTTPCallback, request.getTimeout(), HttpConfig.getHttpConfig().httpHeaderFactory.getHttpHeaders());
break;
case MULTIPART_POST:
LogUtil.e("multipart post not supported yet!");
// not supported yet;
break;
}
我们看到当我们默认的post 的方式:
我们看下里面的具体实现方式:
/**
* 异步调用HTTP的post方法
*
* @param url 目标url
* @param json JSON字符串
* @param responseCallback 回调
* @param timeoutMillis 超时,单位毫秒, 如果超时时间>kMaxTimeout(120*1000ms)或者小于kMinTimeout(5*1000ms),都会使用默认超时时间kDefaultTimeout(30*1000ms)
* @param headers 自定义HTTP Headers
* @return 返回request的Tag,取消请求的时候需要使用
*/
public String asyncPostWithTimeout(String url, String json, final CtripHTTPCallbackV2 responseCallback
, final int timeoutMillis, Map<String, String> headers, boolean isFromCRN) {
String retTag = getRequestTagByURL(url);
// okClient.setReadTimeout(formatTimeout(timeoutMillis), TimeUnit.MILLISECONDS);
Request.Builder builder = new Request.Builder()
.url(url)
.tag(retTag);
RequestBody jsonBody = RequestBody.create(MediaType_JSON, json);
if (headers != null) {
for (String header : headers.keySet()) {
if (TextUtils.equals("Content-Type", header)) {
jsonBody = RequestBody.create(MediaType.parse(headers.get(header)), json);
}
builder.header(header, headers.get(header));
}
}
if(CookieManager.getInstance().getCookie(url)!=null && HttpConfig.getHttpConfig().getHttpRequestInterceptor().autoSetCookie()) {
builder.header("cookie", CookieManager.getInstance().getCookie(url));
}
LogUtil.d("url="+url+"cookie="+CookieManager.getInstance().getCookie(url));
final Request request = builder.post(jsonBody).build();
final Call call = okClient.newCall(request);
CallbackWithTimeout cbWithTimeout = wrapCallbackWithTimeout(call, request, responseCallback, timeoutMillis, isFromCRN);
call.enqueue(cbWithTimeout);
return retTag;
}
我们看到,这里我们是拼装header 的方式,和设置cookie
然后重新写Call ,我们来看下 CallbackWithTimeout 这个类中的具体实现方式,
private CallbackWithTimeout wrapCallbackWithTimeout(final Call call,
final Request request,
final CtripHTTPCallbackV2 responseCallback,
int timeoutMillis,
final boolean isFromCRN) {
if (call == null || request == null) {
return null;
}
final long startWrapTime = System.currentTimeMillis();
final boolean correctingTimeout = true;
final OkHandler handler = new OkHandler(sharedThread.getLooper());
handler.request = request;
handler.call = call;
Message message = Message.obtain();
message.what = 0;
CallbackWithTimeout CallbackWithTimeout = new CallbackWithTimeout() {
@Override
public void onFailure(final Call call, final IOException e) {
handler.removeMessages(0, this);
if (call.isCanceled()) {
return;
}
RequestSaveBean requestSaveBean = requestsHashMap.get(call.request());
synchronized (CtripHTTPClientV2.class) {
requestsHashMap.remove(call.request());
}
try {
if (httpResponseObserver != null) {
httpResponseObserver.onFailed(call.request().url().toString(), e);
}
} catch (Exception e1) {
LogUtil.e("error when do http failed callbacl");
}
if (!mTimeout) {
if (timeoutException.equals(e)) {
mTimeout = true;
}
logHTTPRequestMetrics(request, null, "HTTP Request失败:" + e.getMessage(), requestSaveBean == null ? startWrapTime : requestSaveBean.startTime, isFromCRN);
ThreadUtils.runOnBackgroundThread(new Runnable() {
@Override
public void run() {
try {
if (responseCallback != null) {
CtripHttpFailure failure = new CtripHttpFailure();
failure.setCall(call);
failure.setException(e);
responseCallback.onFailure(failure);
}
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}
@Override
public void onResponse(final Call call, final Response response) throws IOException {
handler.removeMessages(0, this);
if (call.isCanceled()) {
return;
}
if (!mTimeout) {
LogUtil.e("CtripHttpV2", "Response : " + response.toString());
int responseCode=response.code();
if(responseCode==RESPONSE_CODE_431 || responseCode==RESPONSE_CODE_432 || responseCode==RESPONSE_CODE_430 || responseCode==RESPONSE_CODE_429){
HttpConfig.getHttpConfig().httpRequestInterceptor.antiBot(responseCode+"");
return;
}
//Response code非2xx都应该走Failure回调
RequestSaveBean requestSaveBean = requestsHashMap.get(call.request());
synchronized (CtripHTTPClientV2.class) {
requestsHashMap.remove(call.request());
}
if (response != null && response.isSuccessful()) {
logHTTPRequestMetrics(request, response, null, requestSaveBean == null ? startWrapTime : requestSaveBean.startTime, isFromCRN);
if (responseCallback != null) {
final CtripHttpResponse httpResponse = new CtripHttpResponse();
httpResponse.setCall(call);
httpResponse.setResponse(response);
ThreadUtils.runOnBackgroundThread(new Runnable() {
@Override
public void run() {
try {
responseCallback.onResponse(httpResponse);
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
try {
if (httpResponseObserver != null) {
httpResponseObserver.onSuccess(call.request().url().toString());
}
} catch (Exception e1) {
LogUtil.e("error when do http success callbacl");
}
} else {
logHTTPRequestMetrics(request, response, "HTTP Response code:" + response.code(), requestSaveBean == null ? startWrapTime : requestSaveBean.startTime, isFromCRN);
final CtripHttpFailure failure = new CtripHttpFailure();
failure.setCall(call);
failure.setException(new SOAIOExceptionV2("Http Response error", response));
ThreadUtils.runOnBackgroundThread(new Runnable() {
@Override
public void run() {
try {
if (responseCallback != null) {
responseCallback.onFailure(failure);
}
} catch (Exception e) {
e.printStackTrace();
}
}
});
try {
if (httpResponseObserver != null) {
httpResponseObserver.onFailed(call.request().url().toString(), failure.getException());
}
} catch (Exception e1) {
LogUtil.e("error when do http success callbacl");
}
}
}
}
};
message.obj = CallbackWithTimeout;
if (correctingTimeout) {
RequestSaveBean saveBean = new RequestSaveBean();
saveBean.message = message;
saveBean.time = formatTimeout(timeoutMillis);
saveBean.mOkHandler = handler;
saveBean.inqueueTime = System.currentTimeMillis();
requestsHashMap.put(request, saveBean);
} else {
handler.sendMessageDelayed(message, formatTimeout(timeoutMillis));
}
return CallbackWithTimeout;
}
我们看到 代码中设置了handler 我们看到,这是我们的控制,设置了些参数
final OkHandler handler = new OkHandler(sharedThread.getLooper());
handler.request = request;
handler.call = call;
Message message = Message.obtain();
message.what = 0;
这个handler 是什么时候触发的,我们看下代码,在最后一段是
handler.sendMessageDelayed(message, formatTimeout(timeoutMillis));
但是触发条件始终为false ,所以我们不关心这个地方的具体的实现,我们看下call 返回成功做的操作,
if (!mTimeout) {
LogUtil.e("CtripHttpV2", "Response : " + response.toString());
int responseCode=response.code();
if(responseCode==RESPONSE_CODE_431 || responseCode==RESPONSE_CODE_432 || responseCode==RESPONSE_CODE_430 || responseCode==RESPONSE_CODE_429){
HttpConfig.getHttpConfig().httpRequestInterceptor.antiBot(responseCode+"");
return;
}
//Response code非2xx都应该走Failure回调
RequestSaveBean requestSaveBean = requestsHashMap.get(call.request());
synchronized (CtripHTTPClientV2.class) {
requestsHashMap.remove(call.request());
}
if (response != null && response.isSuccessful()) {
logHTTPRequestMetrics(request, response, null, requestSaveBean == null ? startWrapTime : requestSaveBean.startTime, isFromCRN);
if (responseCallback != null) {
final CtripHttpResponse httpResponse = new CtripHttpResponse();
httpResponse.setCall(call);
httpResponse.setResponse(response);
ThreadUtils.runOnBackgroundThread(new Runnable() {
@Override
public void run() {
try {
responseCallback.onResponse(httpResponse);
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
try {
if (httpResponseObserver != null) {
httpResponseObserver.onSuccess(call.request().url().toString());
}
} catch (Exception e1) {
LogUtil.e("error when do http success callbacl");
}
首先我们看到,首先根据返回的状态码,做反爬虫的的处理,然后就是将返回的结果,然后就是将常规的返回response。
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.connectTimeout(kMaxTimeout, TimeUnit.MILLISECONDS);
builder.readTimeout(kMaxTimeout, TimeUnit.MILLISECONDS);
builder.writeTimeout(kMaxTimeout, TimeUnit.MILLISECONDS);
builder.connectionPool(new ConnectionPool(5, 60000L, TimeUnit.MILLISECONDS));
builder.addInterceptor(new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
RequestSaveBean requestSaveBean = requestsHashMap.get(chain.request());
if (requestSaveBean != null) {
requestSaveBean.startTime = System.currentTimeMillis();
requestSaveBean.mOkHandler.sendMessageDelayed(requestSaveBean.message, requestSaveBean.time);
}
try {
Response response = chain.proceed(chain.request());
return response;
} catch (IOException e) {
throw e;
} catch (Exception e) {
e.printStackTrace();
throw new IOException("Other Exception:" + e.getMessage());
}
}
});
可以看到这里是,自定义了一个拦截器。这里的拦截器是处理释放缓存的。
这里推荐一个介绍okHttp 很详细的网站