用.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以及媒体信息解析等部分),包括如何把 ...
随机推荐
- js高阶函数map和reduce
map 举例说明,比如我们有一个函数f(x)=x2,要把这个函数作用在一个数组[1, 2, 3, 4, 5, 6, 7, 8, 9]上,就可以用map实现如下: 由于map()方法定义在JavaScr ...
- 对 Laravel 的 Controller 做 Unit Test
之前尝试过对 Laravel 的 Controller 做 Feature Test,但是在业务变得越来越复杂之后,我感觉对 controller 里的函数也没了自信,急需对功能函数做 Unit Te ...
- STL整理之set
转载请注明出处,部分内容引自李煜东<算法竞赛进阶指南> 前置知识: C++.C语言入门 Set是什么 Set是C++STL中提供的容器,set是数学上的集合——具有唯一性,即每个元素 ...
- python 全栈开发,Day33(tcp协议和udp协议,互联网协议与osi模型,socket概念,套接字(socket)初使用)
先来回顾一下昨天的内容 网络编程开发架构 B/S C/S架构网卡 mac地址网段 ip地址 : 表示了一台电脑在网络中的位置 子网掩码 : ip和子网掩码按位与得到网段 网关ip : 内置在路由器中的 ...
- 《剑指offer》-连续子数组的最大和
题目描述 HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学.今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决.但是,如果向量 ...
- 【Leetcode】404. Sum of Left Leaves
404. Sum of Left Leaves [题目]中文版 英文版 /** * Definition for a binary tree node. * struct TreeNode { * ...
- 【C++ Primer 第10章】 10.4.2 插入迭代器
iostream迭代器 标准库为iostream定义了可用于这些IO类型对象的迭代器. istream_iterator读取输入流, ostream_iterator向一个输出流写数据. 1. i ...
- Codeigniter使用HMVC(一)
涉及三方代码: https://tutorials.kode-blog.com/codeigniter-admin-panel https://bitbucket.org/wiredesignz/co ...
- hdu 5256 最少修改多少个数 能使原数列严格递增 (LIS)
Problem Description我们有一个数列A1,A2...An,你现在要求修改数量最少的元素,使得这个数列严格递增.其中无论是修改前还是修改后,每个元素都必须是整数. 请输出最少需要修改多少 ...
- Nginx 关键字详解
转自: https://blog.csdn.net/zhangliangzi/article/details/78257593 1.[alias]——别名配置,用于访问文件系统,在匹配到locatio ...