用.Net打造一个移动客户端(Android/IOS)的服务端框架NHM(四)——Android端Http访问类(转)
本章目的
在上一章中,我们利用Hibernate Tools完成了Android Model层的建立,依赖Hibernate Tools的强大功能,自动生成了Model层。在本章,我们将继续我们的项目,在Android端完成Http一个访问类,并完成整个请求、处理 Response、反序列化Json的工作。那么首先我们来看下Android的Http访问。
Http, POST or GET?
在 开始正式设计Http访问类之前,我们必须先了解一些关于Http协议的必要知识。在Http1.1规范中,一共定义了8种方法,在这里与我们有关的是 Get、post两种方法,对大多数程序员来说,大致的意思我们都明白,但能完整理解两种方法的区别和联系的,就不那么容易了。这里作者不去从协议本身去 探讨二者区别,只从应用层来讲:
GET:向服务器的特定资源发出请求,这种方法要求所有传递的参数只能通过URL的QueryString,由于URL长度大小最大有2KB限制,所以一般只能传递简单的参数。
POST:向服务器发送数据,这种方法可以将参数包含在请求体中,可以用来传输大量数据,如上传文件等。
如果你想了解HTTP协议的更多细节,这篇文章写的很详细,值得参考:http://www.cnblogs.com/skynet/archive/2010/05/18/1738301.html
Android的Http访问类
http访问类类的部分代码参考了开源的 https://github.com/yusuke
首先我们来看下Android中进行httprequest的全过程
1、根据URL生成成java.net.HttpURLConnection对象
2、设置HttpURLConnection允许返回值
3、设置HttpURLConnection的Header数据(Header数据保存在 Hashmap<String,String> requestHeaders中,从Configration静态类取得)
4、根据不同类型的httpMethod设置HttpURLConnection 的 RequestMethod
-----------至此HttpURLConnection生成完毕----------------
5、通过调用HttpURLConnection.getInputStream()方法实现Http连接
6、将服务器发回来的数据生成为Response对象(此对象由我们封装,表示服务器返回的数据)
7、通过HttpURLConnection.getResponseCode()的值判断此次Http请求是否成功
8、如果成功,HttpURLConnection.getInputStream()即为服务器返回的http流
-----------至此Request请求完毕----------
通过上面的流程,我们可以设计我们的http访问类,最核心的是下面几个类
HttpClientP:处理Http请求,包括重要的httprequest()方法以及设置header等方法
Response:HttpRequest的返回值,包括asDocument(), asJsonArray(), asString()等与取结果流有关方法,以及 getStatusCode()等与取连接状态、头变量相关的方法
Configuration:保存Properties的静态类,设置连接超时时间、重试次数等
下面给出http访问类的主要方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
|
public class HttpClientP implements java.io.Serializable { private static final int OK = 200 ; // OK: Success! public Response httpRequest(String url, PostParameter[] postParams, boolean authenticated, String httpMethod) throws NException { int retriedCount; int retry = retryCount + 1 ; Response res = null ; for (retriedCount = 0 ; retriedCount < retry; retriedCount++) { int responseCode = - 1 ; try { HttpURLConnection con = null ; OutputStream osw = null ; try { con = getConnection(url); con.setDoInput( true ); setHeaders(url, postParams, con, false , httpMethod); if ( null != postParams || "POST" .equals(httpMethod)) { con.setRequestMethod( "POST" ); con.setRequestProperty( "Content-Type" , "application/x-www-form-urlencoded" ); con.setDoOutput( true ); String postParam = "" ; if (postParams != null ) { postParam = encodeParameters(postParams); } log( "Post Params: " , postParam); byte [] bytes = postParam.getBytes( "UTF-8" ); con.setRequestProperty( "Content-Length" , Integer.toString(bytes.length)); osw = con.getOutputStream(); osw.write(bytes); osw.flush(); osw.close(); } else if ( "DELETE" .equals(httpMethod)) { con.setRequestMethod( "DELETE" ); } else { con.setRequestMethod( "GET" ); } res = new Response(con); responseCode = con.getResponseCode(); if (DEBUG) { log( "Response: " ); Map<String, List<String>> responseHeaders = con .getHeaderFields(); for (String key : responseHeaders.keySet()) { List<String> values = responseHeaders.get(key); for (String value : values) { if ( null != key) { log(key + ": " + value); } else { log(value); } } } } if (responseCode != OK) { if (responseCode < INTERNAL_SERVER_ERROR || retriedCount == retryCount) { throw new NException(getCause(responseCode) + "\n" + res.asString(), responseCode); } // will retry if the status code is // INTERNAL_SERVER_ERROR } else { break ; } } finally { try { osw.close(); } catch (Exception ignore) { } } } catch (IOException ioe) { // connection timeout or read timeout if (retriedCount == retryCount) { throw new NException(ioe.getMessage(), ioe, responseCode); } } try { if (DEBUG && null != res) { res.asString(); } log( "Sleeping " + retryIntervalMillis + " millisecs for next retry." ); Thread.sleep(retryIntervalMillis); } catch (InterruptedException ignore) { // nothing to do } } return res; } } |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
public class Response { private int statusCode; private Document responseAsDocument = null ; private String responseAsString = null ; private InputStream is; private HttpURLConnection con; private boolean streamConsumed = false ; public Response() { } public Response(HttpURLConnection con) throws IOException { this .con = con; this .statusCode = con.getResponseCode(); if ( null == (is = con.getErrorStream())){ is = con.getInputStream(); } if ( null != is && "gzip" .equals(con.getContentEncoding())) { // the response is gzipped is = new GZIPInputStream(is); } } } |
服务器端JSON序列化
前文已经说过,服务器采用Newtonsoft.JSON来序列化LINQ数据,这里,我们稍微将LinqToJson扩展一下,以适合Android端的GSON反序列化方法。先看代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
public static string LinqToJson( object o) { string rtn = "" ; //设置Json序列化格式 JsonSerializer js = new JsonSerializer(); //JSON中的Key名称采用驼峰命名法,且首字母小写 js.ContractResolver = new CamelCasePropertyNamesContractResolver(); //设置JSON Date类型转换格式 Newtonsoft.Json.Converters.IsoDateTimeConverter timeConverter = new Newtonsoft.Json.Converters.IsoDateTimeConverter(); timeConverter.DateTimeFormat = "yyyy'-'MM'-'dd' 'HH':'mm':'ss" ; js.Converters.Add(timeConverter); //转换为为Json Array rtn = JArray.FromObject(o, js).ToString(); return rtn; } 与 之前的版本相比,我们主要设置了JSON序列化格式,由于hibernate Tools生成的JavaBean类均采用驼峰命名,且首字母小写,所以要在Newtonsoft.json中设置一个JsonSerializer来实 现匹配。另外,Date类型是必须要重新处理的数据类型。 |
1
|
测试一下: |
1
2
3
4
5
6
7
8
9
|
public partial class Default : System.Web.UI.Page { protected void Page_Load( object sender, EventArgs e) { EmployeeEntity ee = new EmployeeEntity(1); lt_rtn.Text = ee.toJson(); } } |
数据库中的字段,可以看到已经转换字段名为首字母小写了:
客户端Android采用GSON反序列化
有了客户端的Http访问类,我们就可以来访问服务器了,我们采用下面的步骤来进行:
1、新建一个HashMap,包含发送到服务器的QueryString参数。在将来的使用中,通过向HashMap添加K-V对来实现添加QueryString参数
2、将此HashMap转换为一个加密的字符串
3、使用http.get()方法与服务器连接
4、如果出现Exception则进入全局Exception对象处理
5、连接正常的话,反序列化结果为对应对象
6、连接至此完成
现在我们暂时先使用一个Activity来测试这个连接过程,将所有流程都写入OnCreate()中,暂不考虑其他有更有逻辑的封装。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
public class MainActivity extends Activity { /** Called when the activity is first created. */ private String baseURL = Configuration.getServer(); protected HttpClientP http = new HttpClientP(); @Override public void onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.main); TextView tvEmpName = (TextView) this .findViewById(R.id.empName); //设置QueryString参数的HashMap HashMap param = new HashMap(); param.put( "cmd" , "test" ); //将此HashMap转换为加密字符串 String parmstr = URLParamUtils.toURLParam(param); String paramstrall = baseURL+ "default.aspx?a=" + parmstr; try { //使用Http.get()连接 返回JsonArray JSONArray json = get(paramstrall, null , true ).asJSONArray(); String jsonfirst = json.get( 0 ).toString(); //新建Gson对象并设置与服务器发来相同格式的Date类型 Gson gson = new GsonBuilder().setDateFormat( "yyyy-MM-dd HH:mm:ss" ).create(); //反序列化Json数据为 Employees类型 Employees emp = gson.fromJson(jsonfirst, Employees. class ); //测试Employees数据 tvEmpName.setText(emp.getFirstName()); } catch (NException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (JSONException e) { // TODO Auto-generated catch block e.printStackTrace(); } } protected Response get(String url, PostParameter[] params, boolean authenticate) throws NException { if ( null != params && params.length > 0 ) { url += "&" + HttpClientP.encodeParameters(params); } return http.get(url, authenticate); } } |
结果如下
小结与扩展
这一章我们主要解决了移动客户端与服务器使用HTTP访问的问题,我们了解了http访问的整个过程,完成了客户端的HTTP访问类,在最后的例子中我们使用了http.get()方法来与服务器交互,当然我们的访问类也支持post方法。
在接下来的一章,我们将继续扩展HTTP访问的客户端与服务器端,主要解决传输加密、参数等问题。
用.Net打造一个移动客户端(Android/IOS)的服务端框架NHM(四)——Android端Http访问类(转)的更多相关文章
- [.NET] 一步步打造一个简单的 MVC 电商网站 - BooksStore(四)
一步步打造一个简单的 MVC 电商网站 - BooksStore(四) 本系列的 GitHub地址:https://github.com/liqingwen2015/Wen.BooksStore &l ...
- android菜鸟学习笔记20----Android数据存储(四))Android数据库操作
Android内置了一个名为SQLite的关系型数据库,这是一款轻量型的数据库,操作十分简便.SQLite与别的数据库不同的是,它没有数据类型.可以保存任何类型的数据到你所想要保存的任何表的任何列中. ...
- Android 学习笔记之AndBase框架学习(四) 使用封装好的函数实现单,多线程任务
PS:Force Is Meaningless Without Skill 学习内容: 1.使用AndBase实现单线程任务... 2.使用AndBase实现多线程任务... AndBase内部封 ...
- SSM整合 完美支持RESTful(Jsp和客户端<android ios...>)
一 RESTful简介 RESTful是一种网络应用程序的设计风格和开发方式 它结构清晰 符合标准 易于理解 扩展方便 REST 即Representational State Transfer的缩写 ...
- [.NET] 一步步打造一个简单的 MVC 网站 - BooksStore(一)
一步步打造一个简单的 MVC 网站 - BooksStore(一) 本系列的 GitHub地址:https://github.com/liqingwen2015/Wen.BooksStore 简介 主 ...
- [.NET] 一步步打造一个简单的 MVC 电商网站 - BooksStore(二)
一步步打造一个简单的 MVC 电商网站 - BooksStore(二) 本系列的 GitHub地址:https://github.com/liqingwen2015/Wen.BooksStore 前: ...
- [.NET] 一步步打造一个简单的 MVC 电商网站 - BooksStore(三)
一步步打造一个简单的 MVC 电商网站 - BooksStore(三) 本系列的 GitHub地址:https://github.com/liqingwen2015/Wen.BooksStore &l ...
- [.NET] 一步步打造一个简单的 MVC 电商网站 - BooksStore(一)
一步步打造一个简单的 MVC 电商网站 - BooksStore(一) 本系列的 GitHub地址:https://github.com/liqingwen2015/Wen.BooksStore &l ...
- Android 媒体存储服务(一)
Android 媒体存储服务 本文介绍如何在 Android 中,开发者的 APP 如何使用媒体存储服务(包含MediaScanner.MediaProvider以及媒体信息解析等部分),包括如何把 ...
随机推荐
- graphql详解
随着系统业务量的增大不同的应用和系统共同使用着许多的服务api,而随着业务的变化和发展,不同的应用对相同资源的不同使用方法最终会导致需要维护的服务api数量呈现爆炸式的增长,比如我试着跑了下我们自己业 ...
- python 全栈开发,Day13(迭代器,生成器)
一.迭代器 python 一切皆对象 能被for循环的对象就是可迭代对象 可迭代对象: str,list,tuple,dict,set,range 迭代器: f1文件句柄 dir打印该对象的所有操作方 ...
- 2018-2019-2 20165333 《网络对抗技术》 Exp5:MSF基础应用
2018-2019-2 20165333 <网络对抗技术> Exp5:MSF基础应用 实践内容(3.5分) 本实践目标是掌握metasploit的基本应用方式,重点常用的三种攻击方式的思路 ...
- LINQ学习之旅(三)
Linq to Sql语句之Join和Order By Join操作 适用场景:在我们表关系中有一对一关系,一对多关系,多对多关系等.对各个表之间的关系,就用这些实现对多个表的操作. 说明:在Join ...
- 在Centos下用alternative命令切换各个版本的jdk的方法
https://blog.csdn.net/nsrainbow/article/details/43273991 https://blog.csdn.net/yzh_1346983557/articl ...
- python全栈开发day35-线程、协程
一.线程 1.线程 1).什么是线程 线程是cpu调度的最小单位 线程是进程的必要组成单位 一个进程里至少含有一个线程 2).主线程 程序开始运行的视乎,就产生了一个主线程来运行这个程序 3).子线程 ...
- Spring日记_01 之基于maven的Spring环境搭建
阿里云镜像:maven.aliyun.com 添加Spring坐标: Spring 是java组件容器,Java饭馆 使用者可以通过getBean(对象ID) 获得Date对象,而不需要自己去new ...
- PyInstaller打包python脚本的一些心得
PyInstaller打包python脚本的一些心得 因为在公司经常要帮同事做一个从excel表格中提取出需要的内容的重复工作,比较繁琐还容易出错:于是就想着要写个程序,但是同事又不可能在电脑上也装上 ...
- Linux学习之文件系统常用命令(七)
Linux文件系统常用命令 目录 df命令 du命令 fsck命令 dump2fs命令 df命令 df命令 统计文件系统的占有情况,分区用了多少空间,还剩多少空间 df [选项] [挂载点] 选项: ...
- SSL/TLS中间人攻击
准备:kali.xp kali ip:192.168.14.157 目标ip:192.168.14.158 目标网关:192.168.14.2 使用工具:ettercap.sslstrip.arpsp ...