[关闭]
@yangfch3 2016-10-23T16:37:55.000000Z 字数 12706 阅读 3555

AJAX 拾遗

JavaScript


1. 何时使用 POST

在以下情况中,请使用 POST 请求:


2. 使用 GET 请求如何避免请求到缓存文件

为了避免这种情况,请向 URL 添加一个唯一的 ID:

  1. xmlhttp.open("GET","demo_get.asp?option=" + Math.random(),true);
  2. // 给个随机数,保证每次请求都是新的

3. AJAX 上传数据

有时我们不是仅仅需要读取数据,而是要上传数据给服务器进行处理。

  1. xmlhttp.open("GET","demo_get.asp?UName=yangfch3&PSD=6SEF7SAGADAYASDAFH8577",true);
  2. xmlhttp.send();
  1. xmlhttp.open("POST","demo_post.asp",true);
  2. xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");
  3. xmlhttp.send("fname=Bill&lname=Gates");
  4. //--- 上传其他数据,例如文件 ---
  5. var file = document.getElementById('test-input').files[0];
  6. var xhr = new XMLHttpRequest();
  7. xhr.open('POST', 'myserver/uploads');
  8. xhr.setRequestHeader('Content-Type', file.type);
  9. xhr.send(file);

注意:send() 方法的参数就是要发送到的数据。多种格式的数据,都可以作为它的参数,然后通过 POST 上传。send() 的参数与 FormData 一节!


4. 设置请求头

使用 XHR 对象的 setRequestHeader() 方法来设置请求头,两个字符串参数——请求头名、请求头值。

要设置多个请求头键值对,则可多次使用 setRequestHeader() 方法来进行设置。

设置请求头在特定的情况下可以用来实现 跨域

open()之后、send()之前调用该方法。

  1. xmlhttp.open("POST","ajax_test.asp",true);
  2. // 也可以是 POST
  3. xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");
  4. xmlhttp.send("fname=Bill&lname=Gates");

5. url

open() 方法的 url 参数是服务器上文件的地址:该文件可以是任何类型的文件,比如 .txtJSON.xml,或者服务器脚本文件,比如 .asp.php (在传回响应之前,能够在服务器上执行任务,生成数据)。


6. async=false

  1. xmlhttp.open("GET","test1.txt",false);

async=false 表示会直到等到服务器返回 AJAX 请求结果时才会继续往下执行脚本。我们不推荐这样!

何时可以使用 async=false

注意:使用 async=false 时,不能编写 onreadystatechange 回调函数,请直接将执行的语句写到 send() 后面。


7. AJAX 返回值

AJAX 请求的返回值存储在 XHR 对象的 responseText 或者 responseXML 属性内


8. readyState status onreadystatechange

readyStateXHR 对象现在的状态

  • 0,对应常量UNSENT,表示 XMLHttpRequest 实例已经生成,但是 open() 方法还没有被调用。
  • 1,对应常量 OPENED,表示 send() 方法还没有被调用,仍然可以使用 setRequestHeader(),设定 HTTP 请求的头信息。
  • 2,对应常量 HEADERS_RECEIVED,表示 send() 方法已经执行,并且头信息和状态码已经收到。
  • 3,对应常量 LOADING,表示正在接收服务器传来的 body 部分的数据,如果 responseType 属性是 text 或者空字符串,responseText 就会包含已经收到的部分信息。
  • 4,对应常量 DONE,表示服务器数据已经完全接收,或者本次接收已经失败了。

status:服务器返回的状态码,使用 xhr.statusText 获取

onreadystatechange:存储回调函数(或函数名),每当 readyState 属性改变时,就会调用该函数。


9. 通用抽象写法

  1. function loadXMLDoc(url,cfunc){
  2. if (window.XMLHttpRequest){
  3. // code for IE7+, Firefox, Chrome, Opera, Safari
  4. xmlhttp=new XMLHttpRequest();
  5. }else{// code for IE6, IE5
  6. xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
  7. }
  8. xmlhttp.onreadystatechange=cfunc;
  9. xmlhttp.open("GET",url,true);
  10. xmlhttp.send();
  11. }
  12. // 抽象出来两个常变参数:url cfunc
  13. function myFunction(){
  14. loadXMLDoc("/ajax/test1.txt",function(){
  15. if (xmlhttp.readyState==4 && xmlhttp.status==200){
  16. // do something
  17. }
  18. });
  19. }
  20. // 事件发生时触发 myFunction 即可

10. AJAX 服务器页面 - ASPJSPPHP

为了响应前端的 AJAX 请求,后端也要作相应的处理。

当我们向一个 aspx(asp)、jspphp 发起 AJAX 请求,通过 CGI 可以将我们的请求转给特定的程序处理,由 aspx(asp)、jspphp 等的模板编译器(模板引擎)进行处理,然后返回给服务器程序(IISApache等),然后由服务器程序将请求内容返回给用户。

下面这个案例中:我们在输入框输入字母,每次输入即向服务器端的一个 asp 页面发送请求,服务器端处理后给出返回信息,我们再将返回信息(responseText)作为建议信息展示。百度、Google 等搜索引擎也使用 AJAX 的原理实时返回搜索推荐,只是它的后端处理更加复杂。

下面是上面 AJAX 示例请求的 asp 文件的源码

  1. <%
  2. response.expires=-1
  3. dim a(30) '用名字来填充数组
  4. a(1)="Anna"
  5. a(2)="Brittany"
  6. a(3)="Cinderella"
  7. a(4)="Diana"
  8. a(5)="Eva"
  9. a(6)="Fiona"
  10. a(7)="Gunda"
  11. a(8)="Hege"
  12. a(9)="Inga"
  13. a(10)="Johanna"
  14. a(11)="Kitty"
  15. a(12)="Linda"
  16. a(13)="Nina"
  17. a(14)="Ophelia"
  18. a(15)="Petunia"
  19. a(16)="Amanda"
  20. a(17)="Raquel"
  21. a(18)="Cindy"
  22. a(19)="Doris"
  23. a(20)="Eve"
  24. a(21)="Evita"
  25. a(22)="Sunniva"
  26. a(23)="Tove"
  27. a(24)="Unni"
  28. a(25)="Violet"
  29. a(26)="Liza"
  30. a(27)="Elizabeth"
  31. a(28)="Ellen"
  32. a(29)="Wenche"
  33. a(30)="Vicky"
  34. '获得来自 URL q 参数
  35. q=ucase(request.querystring("q"))
  36. '如果 q 大于 0,则查找数组中的所有提示
  37. if len(q)>0 then
  38. hint=""
  39. for i=1 to 30
  40. if q=ucase(mid(a(i),1,len(q))) then
  41. if hint="" then
  42. hint=a(i)
  43. else
  44. hint=hint & " , " & a(i)
  45. end if
  46. end if
  47. next
  48. end if
  49. '如果未找到提示,则输出返回 "no suggestion"
  50. '否则输出正确的值
  51. if hint="" then
  52. response.write("no suggestion")
  53. else
  54. response.write(hint)
  55. end if
  56. %>

PHP 的写法

  1. <?php
  2. // 用名字来填充数组
  3. $a[]="Anna";
  4. $a[]="Brittany";
  5. $a[]="Cinderella";
  6. $a[]="Diana";
  7. $a[]="Eva";
  8. $a[]="Fiona";
  9. $a[]="Gunda";
  10. $a[]="Hege";
  11. $a[]="Inga";
  12. $a[]="Johanna";
  13. $a[]="Kitty";
  14. $a[]="Linda";
  15. $a[]="Nina";
  16. $a[]="Ophelia";
  17. $a[]="Petunia";
  18. $a[]="Amanda";
  19. $a[]="Raquel";
  20. $a[]="Cindy";
  21. $a[]="Doris";
  22. $a[]="Eve";
  23. $a[]="Evita";
  24. $a[]="Sunniva";
  25. $a[]="Tove";
  26. $a[]="Unni";
  27. $a[]="Violet";
  28. $a[]="Liza";
  29. $a[]="Elizabeth";
  30. $a[]="Ellen";
  31. $a[]="Wenche";
  32. $a[]="Vicky";
  33. //获得来自 URL 的 q 参数
  34. $q=$_GET["q"];
  35. //如果 q 大于 0,则查找数组中的所有提示
  36. if (strlen($q) > 0)
  37. {
  38. $hint="";
  39. for($i=0; $i<count($a); $i++)
  40. {
  41. if (strtolower($q)==strtolower(substr($a[$i],0,strlen($q))))
  42. {
  43. if ($hint=="")
  44. {
  45. $hint=$a[$i];
  46. }
  47. else
  48. {
  49. $hint=$hint." , ".$a[$i];
  50. }
  51. }
  52. }
  53. }
  54. // 如果未找到提示,则把输出设置为 "no suggestion"
  55. // 否则设置为正确的值
  56. if ($hint == "")
  57. {
  58. $response="no suggestion";
  59. }
  60. else
  61. {
  62. $response=$hint;
  63. }
  64. //输出响应
  65. echo $response;
  66. ?>

11. AJAX 与数据库

有数据库支持的 AJAX 请求:

一般网站都有数据库支持,我们请求所需的数据一般都是由服务器从数据库拿取,以下为 asp 程序如何从数据库里面拿数据(当然也可以使用 PHP 编写)

  1. <%
  2. response.expires=-1 '防止缓存,保持每次数据都最新'
  3. sql="SELECT * FROM CUSTOMERS WHERE CUSTOMERID="
  4. sql=sql & "'" & request.querystring("q") & "'" '创建数据库查询语句,传入用户输入的查询参数'
  5. set conn=Server.CreateObject("ADODB.Connection")
  6. conn.Provider="Microsoft.Jet.OLEDB.4.0"
  7. conn.Open(Server.Mappath("/db/northwind.mdb"))
  8. set rs=Server.CreateObject("ADODB.recordset")
  9. rs.Open sql,conn
  10. '创建各个用于数据库连接与查询的对象'
  11. response.write("<table>")
  12. do until rs.EOF
  13. for each x in rs.Fields
  14. response.write("<tr><td><b>" & x.name & "</b></td>")
  15. response.write("<td>" & x.value & "</td></tr>")
  16. next
  17. rs.MoveNext
  18. loop
  19. response.write("</table>")
  20. '返回信息表格'
  21. %>

12. AJAX 与 XML

如果我们请求的是 XML 文件,则可以使用 XHRresponseXML 对象来收集返回 XML,再使用 DOM 来解析与操作 XML

  1. xmlDoc = xmlhttp.responseXML;
  2. a = xmlDoc.getElementsByTagName("title");
  3. // ...
  4. // 拿到 XML 后就可以使用 DOM 进行解析操作了

13. getAllResponseHeaders()

xhr 对象的 getAllResponseHeaders() 方法用于拿取 AJAX 请求响应的响应头信息。

不接收参数,返回 HTTP 所有头信息

  1. // 无参数时
  2. xmlhttp.getAllResponseHeaders()
  3. // Date: Sun, 06 Dec 2015 02:03:49 GMT
  4. // ETag: "1c39a9987f8cce1:2b6e"
  5. // Last-Modified: Mon, 29 Jul 2013 17:18:09 GMT
  6. // Server: Microsoft-IIS/6.0 X-Powered-By: ASP.NET
  7. // Content-Type: text/plain
  8. // Accept-Ranges: bytes
  9. // Content-Length: 108

14. getResponseHeader()

接收一个参数——HTTP 头信息的 name 值,返回对应的 value

  1. xmlhttp.getResponseHeader('Last-Modified');
  2. // Mon, 29 Jul 2013 17:18:09 GMT

其他


response

这是 xhr 对象的一个属性,代表返回的 ArrayBufferBlobDocumentJSON对象。response 有三个子属性:


status 与 statusText

这两者有何不同呢?


timeout 与 ontimeout

timeoutxhr 的属性,设定请求的超时限制;
ontimeout:当超时事件发生执行何种动作,一般等于一个函数或者一个函数名

  1. xhr.ontimeout = function () {
  2. console.error("The request for " + urlArg + " timed out.");
  3. };
  4. xhr.timeout = 1000;

open() 的参数

open 方法可以接受五个参数:


send() 的参数与 FormData
 参数类型

发送的参数可以是:空、NULL、数组对象、二进制对象(图片)、表单、文档XMLHTML

 send 二进制数据

发送二进制数据,最好使用 ArrayBufferViewBlob 对象,这使得通过 Ajax 上传文件成为可能。

send() 一个 ArrayBuffer 对象

  1. function sendArrayBuffer() {
  2. var xhr = new XMLHttpRequest();
  3. xhr.open('POST', '/server', true);
  4. xhr.onload = function(e) { ... };
  5. var uInt8Array = new Uint8Array([1, 2, 3]);
  6. xhr.send(uInt8Array.buffer);
  7. }
 FormDate

构造表单对象FormData),send() 上传一个表单对象

这是 HTML5 XMLHttpRequest Level 2 添加的一个新的接口,用于构造表单数据,常见于 AJAX 发送表单数据。

这样与填写表单,submit 没有什么二致。

  1. var formData = new FormData();
  2. // 构造一个 FormData 对象
  3. formData.append('username', '张三');
  4. formData.append('email', 'zhangsan@example.com');
  5. formData.append('birthDate', 1940);
  6. var xhr = new XMLHttpRequest();
  7. xhr.open("POST", "/register");
  8. xhr.send(formData);
  9. // ------
  10. // 使用 `HTML` 表单(`Form`)来提交表单
  11. > <form id='registration' name='registration' action='/register'>
  12. > <input type='text' name='username' value='张三'>
  13. > <input type='email' name='email' value='zhangsan@example.com'>
  14. > <input type='number' name='birthDate' value='1940'>
  15. > <input type='submit' onclick='return sendForm(this.form);'>
  16. > </form>

FormData 也可以将现有表单构造生成(基于 HTMLForm 元素快速建立 FormData 对象),并可以在此基础上 append 表单数据。

  1. var formElement = document.querySelector("form"); // 获取现有表单对象
  2. var formData = new FormData(formElement); // 根据表单对象 new 一个 FormData 对象
  3. formData.append('csrf', 'e69a18d7db1286040586e6da1950128c'); // 为 FormDate 对象添加新的表单信息
  4. var request = new XMLHttpRequest();
  5. request.open("POST", "submitform.php");
  6. request.send(formDate); // send FormDate 数据

FormDateappend 方法:下面的 name 代表的是表单的元素的 name,第二个参数是实际的值,第三个参数是可选的,通常是文件名。

  1. // Files
  2. formData.append(name, file, filename);
  3. // Blobs
  4. formData.append(name, blob, filename);
  5. // Strings
  6. formData.append(name, value);

FormData 对象也能用来模拟 File 控件,进行文件上传。

  1. // HTML
  2. <form id="file-form" action="handler.php" method="POST">
  3. <input type="file" id="file-select" name="photos[]" multiple/>
  4. <button type="submit" id="upload-button">上传</button>
  5. </form>
  6. // JS
  7. // 获取表单元素内的文件
  8. var fileSelect = document.getElementById('file-select');
  9. var files = fileSelect.files; // file对象的files属性,返回一个FileList对象,包含了用户选中的文件。
  10. var formData = new FormData();
  11. for (var i = 0; i < files.length; i++) {
  12. var file = files[i];
  13. if (!file.type.match('image.*')) {
  14. continue;
  15. }
  16. formData.append('photos[]', file, file.name);
  17. }

FormData也可以加入JavaScript生成的文件。


①load

xhr 有一个 load 事件,与 onreadystatechange 事件相似


②error

xhr 有一个 error 属性,该属性一般为一个函数或函数名。用于监听在 xhr 请求发生错误时执行的操作


③abort

abort 方法用来终止已经发出的 HTTP 请求。


④progress

用于请求的进度,是 xhr 对象的 upload 属性的一个子属性,会不断返回上传的进度。这是 xhr 的新特性。

需要搭配 HTML5 的新特性:<pregress> 元素使用

  1. // HTML
  2. <progress min="0" max="100" value="0">0% complete</progress>
  3. // JS
  4. function upload(blobOrFile) {
  5. var xhr = new XMLHttpRequest();
  6. xhr.open('POST', '/server', true);
  7. xhr.onload = function(e) { ... };
  8. // Listen to the upload progress. 'e' is upload event
  9. var progressBar = document.querySelector('progress');
  10. xhr.upload.onprogress = function(e) {
  11. if (e.lengthComputable) {
  12. progressBar.value = (e.loaded / e.total) * 100;
  13. progressBar.textContent = progressBar.value; // Fallback for unsupported browsers.
  14. }
  15. };
  16. xhr.send(blobOrFile);
  17. }
  18. upload(new Blob(['hello world'], {type: 'text/plain'}));


⑤loadend

不论最后 xhr 请求是 loaderror 还是 abort,都会伴随着一个 loadend 事件(不管请求成功与否),loadend 也一般是一个回调函数或函数名


⑥readyStateChange

readyState属性的值发生改变,就会触发readyStateChange事件。

我们可以通过 onReadyStateChange 属性,指定这个事件的回调函数,对不同状态进行不同处理。尤其是当状态变为 4 的时候,表示通信成功,这时回调函数就可以处理服务器传送回来的数据。


几个事件的总结

  1. var xhr = new XMLHttpRequest();
  2. xhr.addEventListener("progress", updateProgress);
  3. xhr.addEventListener("load", transferComplete);
  4. xhr.addEventListener("error", transferFailed);
  5. xhr.addEventListener("abort", transferCanceled);
  6. xhr.open();
  7. function updateProgress (oEvent) {
  8. // ...(见 progress )
  9. }
  10. function transferComplete(evt) {
  11. console.log("The transfer is complete.");
  12. }
  13. function transferFailed(evt) {
  14. console.log("An error occurred while transferring the file.");
  15. }
  16. function transferCanceled(evt) {
  17. console.log("The transfer has been canceled by the user.");
  18. }

跨域

传统 Ajax 只能向当前网页所在的域名发出 HTTP 请求——同域限制。

解决方案:

三个方案各有优缺点,下面分条阐述


* 代理服务器转发

同源域名下架设一个代理服务器来转发,JavaScript 负责把请求发送到代理服务器:

'/proxy?url=http://www.sina.com.cn'

代理服务器再把结果返回,这样就遵守了浏览器的同源策略。这种方式麻烦之处在于需要服务器端额外做开发


* Flash 插件

通过Flash插件发送HTTP请求,这种方式可以绕过浏览器的安全限制,但必须安装Flash,并且跟Flash交互。不过Flash用起来麻烦,而且现在用得也越来越少了。


JSONP

JSONP 就采用在网页中动态插入 script 元素的做法,向服务器请求脚本文件。使用 GET 的方式发送请求,并需要URL 后面加上参数 ?callback=foo,则服务器会将 JSON 数据放到回调 foo 函数内做为参数返回。

许多服务器支持 JSONP 指定回调函数的名称,直接将 JSON 数据放入回调函数的参数。

JSONP 作用过程:

  1. 预先准备好返回数据处理函数 foo(data){...}
  2. Ajax 请求 JSONP
    • URL?callback=foo 则返回 foo(JSONData)
    • URL 不加 ?callback=foo 则返回服务器指定默认 bar(JSONData)
  3. foo(JSONData)bar(JSONData) 做为行内脚本插入到页面,foo(JSONData)bar(JSONData) 立即执行(因为我们已经预先准备好了 foo 函数)

CORS

CORS 的原理其实很简单,就是自动增加一条 HTTP 头信息的查询,询问服务器端,当前请求的域名是否在许可名单之中,以及可以使用哪些 HTTP 动作。

简单跨域请求 的请求头与响应头信息(GETHEAD 以及 Content-Type 类型
application/x-www-form-urlencodedmultipart/form-datatext/plain):

  1. // AJAX Request Header
  2. Origin: http://www.example.com
  3. // AJAX Response Header
  4. Access-Control-Allow-Origin: http://www.example.com
  5. Access-Control-Allow-Methods: POST, GET, OPTIONS
  6. Access-Control-Allow-Headers: X-PINGOTHER
  7. Access-Control-Max-Age: 1728000


含预检的跨域请求 的请求头与响应头信息(PUTDELETE 以及 其他类型如 application/jsonPOST 请求):

  1. // AJAX Prefilght Request Header
  2. OPTIONS /resources/post-here/ HTTP/1.1
  3. Host: www.google.com
  4. Origin: http://www.example.com
  5. Access-Control-Request-Method: POST
  6. Access-Control-Request-Headers: X-PINGOTHER
  7. // AJAX Response Header for Preflight Request
  8. HTTP/1.1 200 OK
  9. Access-Control-Allow-Origin: http://www.example.com
  10. Access-Control-Allow-Methods: POST, GET, OPTIONS
  11. Access-Control-Allow-Headers: X-PINGOTHER
  12. Access-Control-Max-Age: 1728000
  13. // AJAX Request after Passed Preflight

解释

CORS 机制默认不发送 cookieHTTP 认证信息,除非在 Ajax 请求中打开 withCredentials 属性。

  1. var request = new XMLHttpRequest();
  2. request.withCredentials = true;

同时,服务器返回 HTTP 头信息时,也必须打开 Access-Control-Allow-Credentials 选项。否则,浏览器会忽略服务器返回的回应。

Access-Control-Allow-Credentials: true

由于整个过程都是浏览器自动后台完成,不用用户参与,所以对于开发者来说,使用 Ajax 跨域请求与同域请求没有区别,代码完全一样。但是,这需要服务器的支持,所以在使用 CORS 之前,要查看一下所请求的网站是否支持。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注