Ajax技术的核心是XMLHttpRequest对象(简称XHR),可以通过使用XHR对象获取到服务器的数据,然后再通过DOM将数据插入到页面中呈现。虽然名字中包含XML,但Ajax通讯与数据格式无关,所以我们的数据格式可以是XML或JSON等格式
1.1.1 摘要 Ajax技术的核心是XMLHttpRequest对象(简称XHR),可以通过使用XHR对象获取到服务器的数据,然后再通过DOM将数据插入到页面中呈现。虽然名字中包含XML,但Ajax通讯与数据格式无关,所以我们的数据格式可以是XML或JSON等格式。
XMLHttpRequest对象用于在后台与服务器交换数据,具体作用如下:
在不重新加载页面的情况下更新网页 在页面已加载后从服务器请求数据 在页面已加载后从服务器接收数据 在后台向服务器发送数据
1.1.2 正文 XMLHttpRequest是一个JavaScript对象,它是由微软设计,并且被Mozilla、Apple和Google采纳,W3C正在标准化它。它提供了一种简单的方法来检索URL中的数据。
我们要创建一个XMLHttpRequest实例,只需new一个就OK了: 复制代码 代码如下: //// Creates a XMLHttpRequest object. var req = new XMLHttpRequest(); 也许有人会说:“这可不行啊!IE6不支持原始的XHR对象”,确实是这样,我们在后面将会介绍支持IE6或更老版本创建XHR对象的方法。
XMLHttpRequest的用法 在创建XHR对象后,接着我们要调用一个初始化方法open(),它接受五个参数具体定义如下: 复制代码 代码如下: void open( DOMString method, //"GET", "POST", "PUT", "DELETE" DOMString url, optional boolean async, optional DOMString user, optional DOMString password ); 通过上面的定义我们知道open()方法的签名包含五个参数,其中有参数method和url地址是必填的,假设我们针对URL: myxhrtest.aspx发送GET请求获取数据,具体定义如下: 复制代码 代码如下: var req = new XMLHttpRequest(); req.open( "GET", "myxhrtest.aspx", false ); 通过上述代码会启动一个针对myxhrtest.aspx的GET请求,这里有两点要注意:一是URL相对于执行代码的当前页面(使用绝对路径);二是调用open()方法并不会真正发送请求,而只是启动一个请求准备发送。
只能向同一个域中使用相同端口和协议的URL中发送请求;如果URL与启动请求的页面有任何差别,都会引发安全错误。
要真正发送请求要使用send()方法,send()方法接受一个参数,即要作为请求主体发送的数据,如果不需要通过请求主体发送数据,我们必须传递一个null值。在调用send()之后,请求就会被分派到服务器,完整Ajax请求代码如下: 复制代码 代码如下: var req = new XMLHttpRequest(); req.open( "GET", "myxhrtest.aspx", false ); req.send(null); 在发送请求之后,我们需要检查请求是否执行成功,首先可以通过status属性判断,一般来说,可以将HTTP状态代码为200作为成功标志。这时,响应主体内容会保存到responseText中。此外,状态代码为304表示请求的资源并没有被修改,可以直接使用浏览器缓存的数据,Ajax的同步请求代码如下: 复制代码 代码如下: if (req != null) { req.onreadystatechange = function() { if ((req.status >= 200 && req.status < 300) || req.status == 304) { //// Do something. } else { alert("Request was unsuccessful: " + req.status); } }; req.open("GET", "www.myxhrtest.aspx", true); req.send(null); } 前面我们定义了Ajax的同步请求,如果我们发送异步请求,那么在请求过程中javascript代码会继续执行,这时可以通过readyState属性判断请求的状态,当readyState = 4时,表示收到全部响应数据,属性值的定义如下:
readyState值 |
描述 |
0 |
未初始化;尚未调用open()方法 |
1 |
启动;尚未调用send()方法 |
2 |
已发送;但尚未收到响应 |
3 |
接收;已经收到部分响应数据 |
4 |
完成;收到全部响应数据 | 表1 readyState属性值
同步请求:发生请求后,要等待服务器执行完毕才继续执行当前代码。
异步请求:发生请求后,无需等到服务器执行完毕,可以继续执行当前代码。
现在我们要增加判断readyState属性值,当readyState = 4时,表示全部数据接收完成, 所以Ajax的异步请求代码如下: 复制代码 代码如下: if (req != null) { req.onreadystatechange = function() {
//// Checks the asyn request completed or not. if (req.readyState == 4) { if ((req.status >= 200 && req.status < 300) || req.status == 304) { //// Do something. } else { alert("Request was unsuccessful: " + req.status); } } }; req.open("GET", "www.myxhrtest.aspx", true); req.send(null); } Ajax同源请求 现在我们对Ajax的请求实现有了初步的了解,接下来我们将通过具体的例子说明Ajax请求的应用场合和局限。
在日常网络生活中,我们在浏览器的地址中输入要访问的URL并且回车,浏览器会向服务器发送请求,当服务器收到请求后,把相应的请求页面发送回浏览器,我们会发现页面大部分加载完毕,有些还没有加载完毕。总得来说,采用异步加载方式不会影响已加载完毕的页面浏览,我们可以通过Ajax实现异步加载。
这里我们以AdventureWorks数据库为例,把产品表(Product)中的数据通过报表呈现给用户,我们可以通过多种方法实现该报表需求,这里我们将通过Ajax实现该功能。
首先,我们要把后台数据转换为JSON格式,接下来我们定义Product表的数据库访问对象(DAO),具体的实现代码如下: 复制代码 代码如下: /// <summary> /// The product datatable dao. /// </summary> public class ProductDao { /// <summary> /// Initializes a new instance of the <see cref="ProductDao"/> class. /// </summary> public ProductDao() { }
/// <summary> /// Gets or sets the product id. /// </summary> public int Id { get; set; }
/// <summary> /// Gets or sets the product name. /// </summary> public string Name { get; set; }
/// <summary> /// Gets or sets the product serial number. /// </summary> public string SerialNumber { get; set; }
/// <summary> /// Gets or sets the product qty. /// </summary> public short Qty { get; set; } } 前面我们定义了Product表的数据库访问对象——ProductDao,它包含四个属性分别是产品的Id,名称,序列号和销售数量。
接下来,让我们实现Product表的数据库操作类。 复制代码 代码如下: /// <summary> /// Product table data access manager. /// </summary> public class ProductManager { /// <summary> /// The query sql. /// </summary> private const string Query = "SELECT ProductID, Name, ProductNumber, SafetyStockLevel FROM Production.Product";
/// <summary> /// Stores the object of <see cref="ProductDao"/> into list. /// </summary> private IList<ProductDao> _products = new List<ProductDao>();
/// <summary> /// Gets all products in product table. /// </summary> /// <returns> /// The list of <see cref="ProductDao"/> object. /// </returns> public IList<ProductDao> GetAllProducts() { using (var con = new SqlConnection(ConfigurationManager.ConnectionStrings["SQLCONN"].ToString())) using (var com = new SqlCommand(Query, con)) { con.Open(); using (var reader = com.ExecuteReader(CommandBehavior.CloseConnection)) { while (reader.Read()) { var product = new ProductDao { Id = (int)reader["ProductID"], Name = (string)reader["Name"], SerialNumber = (string)reader["ProductNumber"], Qty = (short)reader["SafetyStockLevel"] }; _products.Add(product); } } }
return _products; } } 前面我们实现了Product表的数据库操作类——ProductManager,它包含两个私有字段Quey和_products,还有一个获取Product表中数据的方法——GetAllProducts()。
通过实现ProductDao和ProductManager,而且我们提供GetAllProducts()方法,获取Product表中的数据,接下来我们要调用该方法获取数据。
为了使数据通过JSON格式传递给页面,这里我们要创建一般处理程序(ASHX文件),
一般处理程序适用场合:
创建动态图片 返回REST风格的XML或JSON数据 自定义HTML
<link rel="stylesheet" type="text/css" href="css/WeiboFeed.css"> </head> <body> <div id="jsWeibo"></div> </body> </html>
图8 跨源数据 如上图所示,我们使用$.ajax()方法调用公共微博接口,当成功获取服务器回调数据插入到我们的页面当中。 1.1.3 总结 本文主要介绍了Ajax在同源请求适用性,但在跨源请求中其存在的局限性,进而介绍Ajax和JSONP在跨源请求下解决方法。 回答qianlifeng关于跨源请求的几个问题: 1.一般的跨源不用jsonp请求为什么会报错?和同源的不都是一个请求么?(可能对ajax了解不深) 答:首先跨源请求的解决方法不仅仅有JSON,本文中提及了其他方法,如:表单POST方式、服务器代理、Html5的XDomainRequest和Flash request等;而你提到报错,我觉得你首先要确认数据格式是否正确。关于跨原请求和同源请求本文已经给出了介绍。 2.关于“用Javascript定义回调函数”那块看的不是很明白。传递当前页面的一个js方法给跨源服务器,为什么就能跨源请求了呢?(JSONP?) 服务端根据这个js方法做了什么操作啊? 答:首先我们理解JSON是一种数据格式,而JSONP像似通过一个方法名来封装JSON格式;而跨源请求不是说指定一个回调函数实现的,而是我们利用了浏览器允许跨源请求<script>资源,你也可以我的HTML代码中使用的是Google提供的jQuery库,这也说明了<script>资源可以跨源请求。当我们发送跨源请求后,服务器会返回JSONP,但服务器无法预知接受JSON数据的方法名,所有我们要把函数名告诉(传递)服务器。 复制代码 代码如下: //JSON {"name":"JK_Rush","id":23} //JSONP func({"name":"JK_Rush","id":23}); 3.看你新浪微博的那个例子,是jquery的ajax对跨源做了处理?能不能说说您提到的两种跨源方式的区别或者不同的应用场景,还是随便都一样? 答:是通过$.ajax()方法实现的,如果你想使用动态Javascript实现也可以;至于两种跨源的区别已经在博文中指出了。 回答@On the road....关于JSON反序列化为对象的实现: 答:一般我们可以通过三种方法把JSON数据反序列化为对象,分别是:ASP.NET AJAX中引入的JavaScriptSerializer,WCF中引入的DataContractJsonSerializer,以及Json.NET。 假设,我们获取到员工信息(employee)的JSON数据,它包含两个属性分别是id和复杂属性name,具体如下所示: 复制代码 代码如下: [ { "id": "82105", "name": { "lastName": "Huang", "firstName": "JK" } }, { "id": "82106", "name": { "lastName": "Leung", "firstName": "Cindy" } } ] string data = "[{\"id\":\"82105\",\"fullname\":{\"lastName\":\"Huang\",\"firstName\":\"JK\"}}," + "{\"id\":\"82106\",\"fullname\":{\"lastName\":\"Leung\",\"firstName\":\"Cindy\"}}]"; 根据上述JSON数据的组成,我们定义出相应的对象模型,具体定义如下: 复制代码 代码如下: // The Employee model. public class Employee { public int Id { get; set; } public Name FullName { get; set; } } // The Name model. public class Name { public string FirstName { get; set; } public string LastName { get; set; } } 接下来,我们将介绍使用JavaScriptSerializer,Json.NET和DataContractJsonSerializer反序列化JSON数据为对象。 JavaScriptSerializer 复制代码 代码如下: var serializer = new JavaScriptSerializer(); var employees= serializer.Deserialize<Employee[]>(data);Json.NET using (var stringReader = new StringReader(data)) using (var jsonTextReader = new JsonTextReader(stringReader)) { var serializer = new JsonSerializer(); var employees = serializer.Deserialize<Employee[]>(jsonTextReader); } DataContractJsonSerializer 对于使用WCF的DataContractJsonSerializer方法,我们需要在对象模型添加DataContract和DataMember属性,具体定义如下: 复制代码 代码如下: [DataContract] public class Employee { [DataMember(Name = "id")] public int Id { get; set; } [DataMember(Name = "fullname")] public Name FullName { get; set; } } [DataContract] public class Name { [DataMember(Name = "firstName")] public string FirstName { get; set; } [DataMember(Name = "lastName")] public string LastName { get; set; } } 接着我们使用ReadObjects()方法把JSON数据转换为对象。 复制代码 代码如下: using (MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(data))) { DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(List<Employee>)); var employee = (List<Employee>)serializer.ReadObject(ms); }
参考
https://developer.mozilla.org/en/XMLHttpRequest http://www.w3schools.com/xml/xml_http.asp http://msdn.microsoft.com/en-us/library/windows/apps/cc836466%28v=vs.85%29.aspx http://ntesmailfetc.blog.163.com/blog/static/206287061201241011546581/ http://justcoding.iteye.com/blog/1366102 http://www.queness.com/post/8567/create-a-dead-simple-twitter-feed-with-jquery |