技术实现之http请求封装
2014-09-03 22:27
736 查看
前面的2篇文章对现有工作项目进行了一个简要的介绍,从这篇文章开始,将会对现在项目中用到的开发模式进行详细的总结和概括。
如题,本篇文章将总结项目中http请求的封装。
由于公司的api接口的特殊设计的缘故,为了使得在开发的工程中不至于浪费太多的时间在http请求编写上,所以我对http请求进行了封装。
1、接口的调用方式
所有的api接口都有一个公共的入口 http://192.168.1.23:9000/dataSearch ,ReqHeader部分是特殊的格式,基本上是固定的,而各个接口之间是通过其中的action参数来进
行划分的,而params则表示客户端部分需要向服务端传递的参数,可以理解为get请求中?后面的参数集合。而返回的格式如下
其中 RespHeader是头部信息,客户端调用成功与否是通过RespBody中的rtnCode字段来进行判断的,0表示成功,其余的都表示失败。
2、http请求的封装底层基类
客户端的http请求是采用AsynTask来调用的,所有的http请求都有一个公共的基类DataSearchApiCallAsynTask,代码如下
具体单个接口的调用则是需要new 一个OnDataSearchApiCompleted 回调函数对象,其代码如下
3、http请求的封装类
开始封装上面这个基类时候,因为没有考虑到实际中的调用情况,所以在实际调用的时候有很多重复多余的代码,基于这一点,我再一次对调用进行了封装,新增一个
Abstract类,代码如下
有了以上的基类,在实际写新的接口的时候只需要直接继承这个抽象类,并实现该类中后面的6个抽象方法就可以,接口调用代码真正的精简了不少,看起来也更加舒服。下
面将通过一个实际的例子来调用上面封装好的请求,其中IViewBuilder接口的代码如下
4、接口调用实例
以下通过编写登录接口来,理解上面的封装。登录的基本调用接口类代码,InvokeUserLoginApiBuilder.java
这个类中最重要的一步是定义了一个IFinishInvokeCallback 回调对象,通过该回调对象的方法,客户端可以自行的处理实际的业务逻辑了,其代码如下
其中就只定义了处理成功和处理失败2中情况的方法,之所以onSuccess和onFail方法中的输入参数定义为Object类型,是为了接口的统一,因为在实际中返回的接口可能是
单中类型,如String、int 、double等,也可能是List集合类型,这时候Object类型的好处就显而易见了,只需要进行强转就可以获取实际需要的类型。
5、接口调用
以上讲了这么多,终于轮到业务逻辑的处理了,登录按钮的点击事件调用如下
btnLogin.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View paramView) {
doLoginBusiness();
}
doLoginBusiness调用登录接口的方法如下
6、总结
通过将http请求进行封装,使得单个接口的实现变得更加的简单,同时提高了代码的可阅读性和美观度,以后需要不断的进行尝试封装,减少重复的代码,提高代码的复用。
如题,本篇文章将总结项目中http请求的封装。
由于公司的api接口的特殊设计的缘故,为了使得在开发的工程中不至于浪费太多的时间在http请求编写上,所以我对http请求进行了封装。
1、接口的调用方式
curl http://192.168.1.23:9000/dataSearch -H "Content-Type:application/json;charset:utf-8" -d '{"ReqHeader":{"appId" : "257741","appVersion" : "0.1","deployId" : "257741","sessionId" : "1234567890","appKey" : "1cc201ee-a182-9d1b-8d94-ce1f3dec0661","uHelpVer" : "1.2","reqTime" : "2013-08-21 09:00:00"},"action":"littleGameImeiLogin","params":{"imei":"121231.com","lat":31.24312312,"lon":121.50912342}}'
所有的api接口都有一个公共的入口 http://192.168.1.23:9000/dataSearch ,ReqHeader部分是特殊的格式,基本上是固定的,而各个接口之间是通过其中的action参数来进
行划分的,而params则表示客户端部分需要向服务端传递的参数,可以理解为get请求中?后面的参数集合。而返回的格式如下
{ <span style="white-space:pre"> </span>"RespHeader": { <span style="white-space:pre"> </span> "hostId": null, <span style="white-space:pre"> </span> "result": "success", <span style="white-space:pre"> </span> "errorCode": "0", <span style="white-space:pre"> </span> "errorNote": "成功", <span style="white-space:pre"> </span> "respTime": "2014-06-30 10:43:03" <span style="white-space:pre"> </span>}, "RespBody": { <span style="white-space:pre"> </span> "rtnCode": 0 <span style="white-space:pre"> </span>} }
其中 RespHeader是头部信息,客户端调用成功与否是通过RespBody中的rtnCode字段来进行判断的,0表示成功,其余的都表示失败。
2、http请求的封装底层基类
客户端的http请求是采用AsynTask来调用的,所有的http请求都有一个公共的基类DataSearchApiCallAsynTask,代码如下
package com.uwen.melo.themonkey.http; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.HashMap; import java.util.Map; import org.apache.http.HttpResponse; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.message.BasicHeader; import org.apache.http.params.HttpConnectionParams; import org.apache.http.protocol.HTTP; import org.apache.http.util.EntityUtils; import org.json.JSONException; import org.json.JSONObject; import android.os.AsyncTask; import android.util.Log; import com.uwen.melo.themonkey.common.ActionConstant; import com.uwen.melo.themonkey.model.common.BasicJsonVo; import com.uwen.melo.themonkey.util.CommonUtil; public class DataSearchApiCallAsynTask extends AsyncTask<String, Integer, String> { private OnDataSearchApiCompleted listener; private String action; private Map<String, Object> params; // private String result; HttpClient httpclient; public DataSearchApiCallAsynTask(OnDataSearchApiCompleted listener, String action, Map<String, Object> params) { this.listener = listener; this.action = action; this.params = params; } protected void onProgressUpdate(Integer... progress) { // update progress here } @Override protected String doInBackground(String... requestParams) { int len = requestParams.length; String url = requestParams[0]; Log.i("action=====>", action); Log.d("params=====>", params.toString()); httpclient = new DefaultHttpClient(); // 设置超时 int connectOverTime = 2000; int requestOverTime = 5000; if (len >= 3) { if (requestParams[1] != null) { connectOverTime = Integer.parseInt(requestParams[1]); // 链接超时时间 } if (requestParams[2] != null) { requestOverTime = Integer.parseInt(requestParams[2]); // 链接超时时间 } } httpclient.getParams().setIntParameter(HttpConnectionParams.SO_TIMEOUT,connectOverTime); // 超时设置 httpclient.getParams().setIntParameter(HttpConnectionParams.CONNECTION_TIMEOUT, requestOverTime);// 连接超时 HttpPost post = new HttpPost(url); Map<String, String> reqHeader = getReqHeader(); Map<String, Object> mp = new HashMap<String, Object>(); mp.put("ReqHeader", reqHeader); mp.put("action", action); mp.put("params", params); String reqHeaderJSonStr = new JSONObject(reqHeader).toString(); String paramsJsonStr = new JSONObject(params).toString(); String actionJsonStr = "\"" + action + "\""; String reqJson = "{" + "\"ReqHeader\" : " + reqHeaderJSonStr + ", \"params\" : " + paramsJsonStr + ", \"action\" : " + actionJsonStr + "}"; String resultStr = null; try { JSONObject jsonObj = new JSONObject(reqJson); StringEntity se = new StringEntity(jsonObj.toString(), "UTF-8"); se.setContentEncoding(new BasicHeader(HTTP.CONTENT_TYPE, "application/json;charset=utf-8")); post.addHeader("Content-Type", "application/json;charset=utf-8"); post.setEntity(se); HttpResponse response = httpclient.execute(post); // int code = response.getStatusLine().getStatusCode(); resultStr = EntityUtils.toString(response.getEntity(), "UTF-8"); } catch (UnsupportedEncodingException e) { System.out.println("连接超时UnsupportedEncodingException"); resultStr = CommonUtil.getBasicHttpJsonStr( ActionConstant.CONNECT_FAILED, "连接错误UnsupportedEncodingException"); } catch (ClientProtocolException e) { System.out.println("连接超时ClientProtocolException"); resultStr = CommonUtil.getBasicHttpJsonStr( ActionConstant.CONNECT_TIME_OUT, "请求超时"); } catch (IllegalStateException e) { System.out.println("进入了连接错误IllegalStateException"); resultStr = CommonUtil.getBasicHttpJsonStr( ActionConstant.CONNECT_FAILED, "连接错误IllegalStateException"); e.printStackTrace(); } catch (JSONException e) { System.out.println("进入了连接错误JSONException"); resultStr = CommonUtil.getBasicHttpJsonStr( ActionConstant.CONNECT_FAILED, "连接错误JSONException"); } catch (IOException e) { System.out.println("进入了连接错误IOException"); resultStr = CommonUtil.getBasicHttpJsonStr( ActionConstant.CONNECT_FAILED, "连接错误IOException"); } return resultStr; } // called after doInBackground finishes @Override protected void onPostExecute(String result) { try { // System.out.println("onPostExecute result=====>"+result); JSONObject jsonObject = new JSONObject(result); if (!jsonObject.has("state")) { jsonObject.put("state", ActionConstant.CONNECT_OK); } listener.onDataTaskInvokeCompleted(jsonObject); } catch (JSONException e) { JSONObject errJson = null; try { /** { "RespHeader": { "hostId": null, "result": "success", "errorCode": "0", "errorNote": "成功", "respTime": "2014-06-30 10:43:03" }, "RespBody": { "rtnCode": 0 } } */ // 构造一个错误的Json格式信息 String errorStr = " { \"RespHeader\": { \"hostId\": null, \"result\": \"failure\", \"errorCode\": \"-1111\", \"errorNote\": \"" + result + "\", \"respTime\": \"2014-06-30 10:43:03\" }, \"RespBody\": { \"rtnCode\": -1111 } }"; errJson = new JSONObject(errorStr); } catch (JSONException e1) { e1.printStackTrace(); } // 错误json回调 listener.onDataTaskInvokeCompleted(errJson); } } public static void testJSON() { BasicJsonVo basicJson = new BasicJsonVo(200, "成功"); try { System.out.println("basic json====>" + basicJson.toString()); JSONObject obj = new JSONObject(basicJson.toString()); System.out.println("测试的json====>" + obj); } catch (JSONException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public static Map<String, String> getReqHeader() { Map<String, String> reqHeader = new HashMap<String, String>(); reqHeader.put("appId", "257741"); reqHeader.put("appVersion", "0.1"); reqHeader.put("deployId", "257741"); reqHeader.put("sessionId", "1234567890"); reqHeader.put("appKey", "1cc201ee-a182-9d1b-8d94-ce1f3dec0661"); reqHeader.put("uHelpVer", "1.2"); reqHeader.put("reqTime", null); return reqHeader; } /** * 取消网络请求 */ public void shutdownHttpClient() { if (httpclient != null) { // 这个shutdown并不是将手机网络断掉,而是将建立Http连接请求时所分配的资源释放掉 httpclient.getConnectionManager().shutdown(); } } }
具体单个接口的调用则是需要new 一个OnDataSearchApiCompleted 回调函数对象,其代码如下
package com.uwen.melo.themonkey.http; import org.json.JSONObject; public interface OnDataSearchApiCompleted{ /** * 执行异步线程回调之后的方法 * @param result */ void onDataTaskInvokeCompleted(JSONObject result); }
3、http请求的封装类
开始封装上面这个基类时候,因为没有考虑到实际中的调用情况,所以在实际调用的时候有很多重复多余的代码,基于这一点,我再一次对调用进行了封装,新增一个
Abstract类,代码如下
package com.uwen.melo.themonkey.views.creates.impl.base; import java.util.Map; import org.json.JSONObject; import android.content.Context; import com.uwen.melo.themonkey.common.ActionConstant; import com.uwen.melo.themonkey.common.CommonInfo; import com.uwen.melo.themonkey.http.DataSearchApiCallAsynTask; import com.uwen.melo.themonkey.http.OnDataSearchApiCompleted; import com.uwen.melo.themonkey.parse.ParseJsonServcie; import com.uwen.melo.themonkey.util.CommonUtil; import com.uwen.melo.themonkey.util.ViewUtil; import com.uwen.melo.themonkey.views.exceptions.RuntimeMessageException; import com.uwen.melo.themonkey.views.interfaze.IViewBuilder; import com.uwen.melo.themonkey.views.interfaze.SimpleCallback; /** * 所有网络请求解析的基类 * @author liao * */ public abstract class AbstractInvokeApiBuilder implements IViewBuilder{ @Override public void build() { init(); } @Override public void init() { invoke(); } @Override public void release() { } /** *api的入口 * @param finalRp */ public void invoke() { final Map<String, Object> params = buildMap(); this.doBusinessByNet(new SimpleCallback() { @Override public void onDone(int state, Object result) { //如果是离线登录 if(CommonInfo.isWithoutWifi()) { handleOtherRtnCode(ActionConstant.WITHOU_TWIFI); return; } try{ if(ActionConstant.CONNECT_OK == state){ try { doParseResponse(result); } catch (Exception e) { e.printStackTrace(); throw e; } }else if(ActionConstant.CONNECT_TIME_OUT == state){ throw new RuntimeMessageException("网络连接超时"); }else{ throw new RuntimeMessageException("未知异常,请联系管理员"); } }catch(Exception e){ ViewUtil.handlerNormalException(getBusinessContext(),e); } } },params); } /** * 解析返回的参数 * @param result * @throws Exception */ private void doParseResponse(Object result) throws Exception { if(!(result instanceof JSONObject)){ responseEmptyData(); } JSONObject jsonObj = (JSONObject)result; int rtnCode = jsonObj.getInt("rtnCode"); System.out.println("current RtnCode ===>"+rtnCode); if (rtnCode == 0) { //增加成功 handleSuccessRtnCode(jsonObj); }else{ handleOtherRtnCode(rtnCode); } } /** * 调用接口的网络操作 * @param finishCallbcak * @param params */ private void doBusinessByNet(final SimpleCallback finishCallbcak, Map<String, Object> params) { try { new DataSearchApiCallAsynTask(new OnDataSearchApiCompleted() { @Override public void onDataTaskInvokeCompleted(JSONObject result) { int state = -1; JSONObject jsonObj = null; try { state = result.getInt("state"); if (state == ActionConstant.CONNECT_OK) { jsonObj = ParseJsonServcie.getResponBodyJson(result); } } catch (Exception e) { state = -1; } finally{ if(null != finishCallbcak){ finishCallbcak.onDone(state, jsonObj); } } } }, getActionName(), params) .execute( CommonUtil.getDataSearchUrl(getBusinessContext()), ActionConstant.DEFAULT_CONNECT_OVER_TIME, ActionConstant.DEFAULT_REQUEST_OVER_TIME ); } catch (Exception e) { if(null != finishCallbcak){ finishCallbcak.onDone(-1, null); } } } /** * 获取context对象,必须重写 * @return */ public abstract Context getBusinessContext(); /** *请求的参数 ,必须重写 * @return */ public abstract Map<String, Object> buildMap() ; /** * action的名字,必须重写 * @return */ public abstract String getActionName(); /** * 返回空数据的处理,可以不做任何处理 */ public abstract void responseEmptyData(); /** * 处理正确返回时候的json数据,必须重写 * @param jsonResult */ public abstract void handleSuccessRtnCode(JSONObject jsonResult); /** * 处理其他返回码的数据,必须重写 * @param jsonResult */ public abstract void handleOtherRtnCode(int rtnCode); }
有了以上的基类,在实际写新的接口的时候只需要直接继承这个抽象类,并实现该类中后面的6个抽象方法就可以,接口调用代码真正的精简了不少,看起来也更加舒服。下
面将通过一个实际的例子来调用上面封装好的请求,其中IViewBuilder接口的代码如下
package com.uwen.melo.themonkey.views.interfaze; public interface IViewBuilder { void build(); void init(); void release(); }
4、接口调用实例
以下通过编写登录接口来,理解上面的封装。登录的基本调用接口类代码,InvokeUserLoginApiBuilder.java
</pre><p></p><pre>
</pre><pre name="code" class="plain">package com.uwen.melo.themonkey.views.creates.impl.user; import java.util.HashMap; import java.util.Map; import org.apache.commons.lang3.StringUtils; import org.json.JSONException; import org.json.JSONObject; import android.app.Activity; import android.content.Context; import com.uwen.melo.themonkey.common.ActionConstant; import com.uwen.melo.themonkey.common.CommonInfo; import com.uwen.melo.themonkey.common.Constant; import com.uwen.melo.themonkey.model.user.LoginApiRtnVO; import com.uwen.melo.themonkey.util.CommonUtil; import com.uwen.melo.themonkey.views.creates.impl.base.AbstractInvokeApiBuilder; import com.uwen.melo.themonkey.views.interfaze.IFinishInvokeCallback; /** * 调用游戏初始化接口 * @author liao * */ public class InvokeUserLoginApiBuilder extends AbstractInvokeApiBuilder { private Activity activity; private IFinishInvokeCallback callback; private String userName; private String passwrod; public InvokeUserLoginApiBuilder(){ }; public InvokeUserLoginApiBuilder(IFinishInvokeCallback callback,Activity activity, String nickName,String password){ this.callback = callback; this.activity= activity; this.userName= nickName; this.passwrod = password; } @Override public void responseEmptyData() { } @Override public void handleSuccessRtnCode(JSONObject jsonResult) { doSuccesBusiness(jsonResult); } @Override public void handleOtherRtnCode(int rtnCode) { doFailBusiness(rtnCode); } @Override public Context getBusinessContext() { return activity; } @Override public Map<String, Object> buildMap() { Map<String, Object> map = new HashMap<String, Object>(); map.put("userName", this.userName); map.put("password", this.passwrod); return map; } @Override public String getActionName() { return ActionConstant.LITTLE_GAME_ACTION_USER_LOGIN; } /*"10001":{"result":"falue","errorCode":"10001","errorNote":"传入的用户id非法","rtnCode":10001}, "10002":{"result":"falue","errorCode":"10002","errorNote":"传入的用户昵称非法","rtnCode":10002}, "10004":{"result":"falue","errorCode":"10004","errorNote":"用户不存在","rtnCode":10004}, "10005":{"result":"falue","errorCode":"10005","errorNote":"密码错误","rtnCode":10005}, "11001":{"result":"falue","errorCode":"11001","errorNote":"用户名过长","rtnCode":11001}, "11002":{"result":"falue","errorCode":"11002","errorNote":"密码不能少于6位","rtnCode":11002}, "-1":{"result":"falue","errorCode":"-1","errorNote":"查询数据出错","rtnCode":-1}, "0":{"result":"success","errorCode":"","errorNote":"登录成功","rtnCode":0} */ private void doFailBusiness(int rtnCode) { String msg = ""; if(rtnCode==10001|| rtnCode ==10002){ msg ="客户端传入参数异常"; }else if(rtnCode==10004){ msg ="用户不存在"; }else if(rtnCode==10005){ msg ="密码错误"; }else if(rtnCode==11001){ msg = "用户名过长"; }else if(rtnCode==11002){ msg = "密码不能少于6位"; }else{ msg = "服务端查询数据出错"; } callback.onFail(msg); } /** * 成功之后的处理 * @param jsonResult */ private void doSuccesBusiness(JSONObject jsonResult) { try { JSONObject userInfo = jsonResult.getJSONObject("UserInfo"); LoginApiRtnVO vo =null; if(userInfo!=null){ vo = new LoginApiRtnVO(); int tmp = CommonUtil.getJSONKeyIntValue(userInfo, "goldNum",Constant.NUMBER_ZERO); vo.setGoldNum(tmp); tmp = CommonUtil.getJSONKeyIntValue(userInfo, "diamondNum",Constant.NUMBER_ZERO); vo.setDiamondNum(tmp); tmp = CommonUtil.getJSONKeyIntValue(userInfo, "level",Constant.NUMBER_ZERO); vo.setLevel(tmp); vo.setUserId(CommonInfo.USER_ID); String nickName = CommonUtil.getJSONKeyValue(userInfo, "nickName",Constant.DEFAULT_EMPTY_STRING_2); vo.setNickName(nickName); String signature = CommonUtil.getJSONKeyValue(userInfo, "personalSignature", Constant.DEFAULT_EMPTY_STRING_2); if(StringUtils.isEmpty(signature)){ signature = "这家伙很懒,什么都没留下!"; } vo.setPersonalSignature(signature); int continueLoginDays = CommonUtil.getJSONKeyIntValue(jsonResult, "continueLoginDays",Constant.NUMBER_ZERO); vo.setContinueLoginDays(continueLoginDays); } callback.onSuccess(vo); } catch (JSONException e) { e.printStackTrace(); } } }
这个类中最重要的一步是定义了一个IFinishInvokeCallback 回调对象,通过该回调对象的方法,客户端可以自行的处理实际的业务逻辑了,其代码如下
package com.uwen.melo.themonkey.views.interfaze; /** * 调用接口完成之后的回调接口 * @author user */ public interface IFinishInvokeCallback { void onSuccess(Object object); void onFail(Object object); }
其中就只定义了处理成功和处理失败2中情况的方法,之所以onSuccess和onFail方法中的输入参数定义为Object类型,是为了接口的统一,因为在实际中返回的接口可能是
单中类型,如String、int 、double等,也可能是List集合类型,这时候Object类型的好处就显而易见了,只需要进行强转就可以获取实际需要的类型。
5、接口调用
以上讲了这么多,终于轮到业务逻辑的处理了,登录按钮的点击事件调用如下
btnLogin.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View paramView) {
doLoginBusiness();
}
doLoginBusiness调用登录接口的方法如下
/** * 调用api接口 */ protected void invokeUserLoginApi() { new InvokeUserLoginApiBuilder(new IFinishInvokeCallback() { @Override public void onSuccess(Object object) { showHideLoadingBar(false); LoginApiRtnVO vo = (LoginApiRtnVO) object; CommonInfo.LOGIN_USER_INFO = vo; //设置在常规变量中 rememberAccount(); //记住用户名和密码 System.out.println(new Gson().toJson(vo)); if(StringUtils.isEmpty(vo.getNickName())){ CommonUtil.gotoActivity(activity, SelectAreaActivity.class,false); }else{ CommonUtil.gotoActivity(activity, MainActivity.class,false); } } @Override public void onFail(Object object) { ViewUtil.createHintDialogWindow(activity, object.toString()); showHideLoadingBar(false); } },activity,userName,password).build(); }至此一个登录的实例终于完成
6、总结
通过将http请求进行封装,使得单个接口的实现变得更加的简单,同时提高了代码的可阅读性和美观度,以后需要不断的进行尝试封装,减少重复的代码,提高代码的复用。
相关文章推荐
- HttpURLConnection+AsyncTask+接口回调实现简易get联网请求封装框架
- HTTP代理实现请求报文的拦截与篡改4--从客户端读取请求报文并封装
- Android中基于HTTP的通信技术(5)Google开源库 使用 Volley 实现 JSON 字符串请求
- 通过HttpWebRequest 发送 POST 请求实现自动登陆
- C#实现通过HttpWebRequest发送POST请求实现网站自动登陆
- C#实现http协议支持上传下载文件的GET、POST请求
- java设置HTTP协议请求(实现断点续传)
- 一个比较好用的socket 类封装(封装http 请求)
- thunk技术实现窗口类的封装
- SOA服务的基本实现方法—使用HTTP协议传输XML请求(POX-over-HTTP)
- C#实现http协议支持上传下载文件的GET、POST请求
- 通过HttpWebRequest 发送 POST 请求实现自动登陆
- 通过HttpWebRequest 发送 POST 请求实现自动登陆
- 链接地址无文件名的实现方式----URL重写技术http://hi.baidu.com/fuyujiang/blog/item/fdf8830aed20923ab0351d12.html
- 通过HttpWebRequest 发送 POST 请求实现自动登陆
- Socket HTTP页面请求后对gzip页面的解压缩实现代码
- Squid+MRTG实现完善的缓存代理和http服务加速代理 -----squid真是个老技术了。。。。
- 【web】http长连接技术(3)http push的jsp实现
- .NET实现HTTP协议中的GET、POST请求
- 通过HttpWebRequest 发送 POST 请求实现自动登陆