@yangfch3
2016-10-23T16:37:55.000000Z
字数 12706
阅读 6334
JavaScript
POST在以下情况中,请使用 POST 请求:
POST 没有数据量限制)POST 比 GET 更稳定也更可靠GET 请求如何避免请求到缓存文件为了避免这种情况,请向 URL 添加一个唯一的 ID:
xmlhttp.open("GET","demo_get.asp?option=" + Math.random(),true);// 给个随机数,保证每次请求都是新的
有时我们不是仅仅需要读取数据,而是要上传数据给服务器进行处理。
GET
xmlhttp.open("GET","demo_get.asp?UName=yangfch3&PSD=6SEF7SAGADAYASDAFH8577",true);xmlhttp.send();
POST HTML 表单那样 POST 数据,请使用 setRequestHeader() 来添加 HTTP 头(格式请见下面示例)。然后在 send() 方法中规定您希望发送的数据(name1=value1&name2=value2):
xmlhttp.open("POST","demo_post.asp",true);xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");xmlhttp.send("fname=Bill&lname=Gates");//--- 上传其他数据,例如文件 ---var file = document.getElementById('test-input').files[0];var xhr = new XMLHttpRequest();xhr.open('POST', 'myserver/uploads');xhr.setRequestHeader('Content-Type', file.type);xhr.send(file);
注意:send() 方法的参数就是要发送到的数据。多种格式的数据,都可以作为它的参数,然后通过 POST 上传。见 send() 的参数与 FormData 一节!
使用 XHR 对象的 setRequestHeader() 方法来设置请求头,两个字符串参数——请求头名、请求头值。
要设置多个请求头键值对,则可多次使用 setRequestHeader() 方法来进行设置。
设置请求头在特定的情况下可以用来实现 跨域!
在 open()之后、send()之前调用该方法。
xmlhttp.open("POST","ajax_test.asp",true);// 也可以是 POSTxmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");xmlhttp.send("fname=Bill&lname=Gates");
open() 方法的 url 参数是服务器上文件的地址:该文件可以是任何类型的文件,比如 .txt 、JSON 和 .xml,或者服务器脚本文件,比如 .asp 和 .php (在传回响应之前,能够在服务器上执行任务,生成数据)。
xmlhttp.open("GET","test1.txt",false);
async=false 表示会直到等到服务器返回 AJAX 请求结果时才会继续往下执行脚本。我们不推荐这样!
何时可以使用 async=false
注意:使用
async=false时,不能编写onreadystatechange回调函数,请直接将执行的语句写到send()后面。
AJAX 请求的返回值存储在 XHR 对象的 responseText 或者 responseXML 属性内
responseText:获得字符串形式的响应数据;responseXML 获得 XML 形式的响应数据。readyState status onreadystatechangereadyState:XHR 对象现在的状态
- 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 属性改变时,就会调用该函数。
function loadXMLDoc(url,cfunc){if (window.XMLHttpRequest){// code for IE7+, Firefox, Chrome, Opera, Safarixmlhttp=new XMLHttpRequest();}else{// code for IE6, IE5xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");}xmlhttp.onreadystatechange=cfunc;xmlhttp.open("GET",url,true);xmlhttp.send();}// 抽象出来两个常变参数:url cfuncfunction myFunction(){loadXMLDoc("/ajax/test1.txt",function(){if (xmlhttp.readyState==4 && xmlhttp.status==200){// do something}});}// 事件发生时触发 myFunction 即可
ASP、JSP 和 PHP为了响应前端的 AJAX 请求,后端也要作相应的处理。
当我们向一个 aspx(asp)、jsp 或 php 发起 AJAX 请求,通过 CGI 可以将我们的请求转给特定的程序处理,由 aspx(asp)、jsp 或 php 等的模板编译器(模板引擎)进行处理,然后返回给服务器程序(IIS、Apache等),然后由服务器程序将请求内容返回给用户。
下面这个案例中:我们在输入框输入字母,每次输入即向服务器端的一个 asp 页面发送请求,服务器端处理后给出返回信息,我们再将返回信息(responseText)作为建议信息展示。百度、Google 等搜索引擎也使用 AJAX 的原理实时返回搜索推荐,只是它的后端处理更加复杂。


下面是上面 AJAX 示例请求的 asp 文件的源码
<%response.expires=-1dim a(30) '用名字来填充数组a(1)="Anna"a(2)="Brittany"a(3)="Cinderella"a(4)="Diana"a(5)="Eva"a(6)="Fiona"a(7)="Gunda"a(8)="Hege"a(9)="Inga"a(10)="Johanna"a(11)="Kitty"a(12)="Linda"a(13)="Nina"a(14)="Ophelia"a(15)="Petunia"a(16)="Amanda"a(17)="Raquel"a(18)="Cindy"a(19)="Doris"a(20)="Eve"a(21)="Evita"a(22)="Sunniva"a(23)="Tove"a(24)="Unni"a(25)="Violet"a(26)="Liza"a(27)="Elizabeth"a(28)="Ellen"a(29)="Wenche"a(30)="Vicky"'获得来自 URL 的 q 参数q=ucase(request.querystring("q"))'如果 q 大于 0,则查找数组中的所有提示if len(q)>0 thenhint=""for i=1 to 30if q=ucase(mid(a(i),1,len(q))) thenif hint="" thenhint=a(i)elsehint=hint & " , " & a(i)end ifend ifnextend if'如果未找到提示,则输出返回 "no suggestion"'否则输出正确的值if hint="" thenresponse.write("no suggestion")elseresponse.write(hint)end if%>
PHP 的写法
<?php// 用名字来填充数组$a[]="Anna";$a[]="Brittany";$a[]="Cinderella";$a[]="Diana";$a[]="Eva";$a[]="Fiona";$a[]="Gunda";$a[]="Hege";$a[]="Inga";$a[]="Johanna";$a[]="Kitty";$a[]="Linda";$a[]="Nina";$a[]="Ophelia";$a[]="Petunia";$a[]="Amanda";$a[]="Raquel";$a[]="Cindy";$a[]="Doris";$a[]="Eve";$a[]="Evita";$a[]="Sunniva";$a[]="Tove";$a[]="Unni";$a[]="Violet";$a[]="Liza";$a[]="Elizabeth";$a[]="Ellen";$a[]="Wenche";$a[]="Vicky";//获得来自 URL 的 q 参数$q=$_GET["q"];//如果 q 大于 0,则查找数组中的所有提示if (strlen($q) > 0){$hint="";for($i=0; $i<count($a); $i++){if (strtolower($q)==strtolower(substr($a[$i],0,strlen($q)))){if ($hint==""){$hint=$a[$i];}else{$hint=$hint." , ".$a[$i];}}}}// 如果未找到提示,则把输出设置为 "no suggestion"// 否则设置为正确的值if ($hint == ""){$response="no suggestion";}else{$response=$hint;}//输出响应echo $response;?>
有数据库支持的 AJAX 请求:

一般网站都有数据库支持,我们请求所需的数据一般都是由服务器从数据库拿取,以下为 asp 程序如何从数据库里面拿数据(当然也可以使用 PHP 编写)
<%response.expires=-1 '防止缓存,保持每次数据都最新'sql="SELECT * FROM CUSTOMERS WHERE CUSTOMERID="sql=sql & "'" & request.querystring("q") & "'" '创建数据库查询语句,传入用户输入的查询参数'set conn=Server.CreateObject("ADODB.Connection")conn.Provider="Microsoft.Jet.OLEDB.4.0"conn.Open(Server.Mappath("/db/northwind.mdb"))set rs=Server.CreateObject("ADODB.recordset")rs.Open sql,conn'创建各个用于数据库连接与查询的对象'response.write("<table>")do until rs.EOFfor each x in rs.Fieldsresponse.write("<tr><td><b>" & x.name & "</b></td>")response.write("<td>" & x.value & "</td></tr>")nextrs.MoveNextloopresponse.write("</table>")'返回信息表格'%>
XML如果我们请求的是 XML 文件,则可以使用 XHR 的 responseXML 对象来收集返回 XML,再使用 DOM 来解析与操作 XML
xmlDoc = xmlhttp.responseXML;a = xmlDoc.getElementsByTagName("title");// ...// 拿到 XML 后就可以使用 DOM 进行解析操作了
xhr 对象的 getAllResponseHeaders() 方法用于拿取 AJAX 请求响应的响应头信息。
不接收参数,返回 HTTP 所有头信息
// 无参数时xmlhttp.getAllResponseHeaders()// Date: Sun, 06 Dec 2015 02:03:49 GMT// ETag: "1c39a9987f8cce1:2b6e"// Last-Modified: Mon, 29 Jul 2013 17:18:09 GMT// Server: Microsoft-IIS/6.0 X-Powered-By: ASP.NET// Content-Type: text/plain// Accept-Ranges: bytes// Content-Length: 108
接收一个参数——HTTP 头信息的 name 值,返回对应的 value
xmlhttp.getResponseHeader('Last-Modified');// Mon, 29 Jul 2013 17:18:09 GMT
这是 xhr 对象的一个属性,代表返回的 ArrayBuffer、Blob、Document、JSON对象。response 有三个子属性:
responseType、
"":字符串(默认值)
"arraybuffer":ArrayBuffer对象
"blob":Blob对象(二进制数据:例如图片)
"document":Document对象(HTML、XML)
"json":JSON对象
"text":字符串
responseText
responseXML这两者有何不同呢?
status 属性表示的是状态码,一个数字,例如:200statusText 属性表示的是状态提示(包括状态码和说明),是一个字符串,例如:"200 OK"timeout:xhr 的属性,设定请求的超时限制;
ontimeout:当超时事件发生执行何种动作,一般等于一个函数或者一个函数名
xhr.ontimeout = function () {console.error("The request for " + urlArg + " timed out.");};xhr.timeout = 1000;
open 方法可以接受五个参数:
GET,也可用于 POST;body 部分)发送的参数可以是:空、NULL、数组对象、二进制对象(图片)、表单、文档(XML、HTML)
发送二进制数据,最好使用 ArrayBufferView 或 Blob 对象,这使得通过 Ajax 上传文件成为可能。
send() 一个 ArrayBuffer 对象
function sendArrayBuffer() {var xhr = new XMLHttpRequest();xhr.open('POST', '/server', true);xhr.onload = function(e) { ... };var uInt8Array = new Uint8Array([1, 2, 3]);xhr.send(uInt8Array.buffer);}
构造表单对象(FormData),send() 上传一个表单对象
这是 HTML5 XMLHttpRequest Level 2 添加的一个新的接口,用于构造表单数据,常见于 AJAX 发送表单数据。
这样与填写表单,submit 没有什么二致。
FormData 和 AJAX 提交表单
var formData = new FormData();// 构造一个 FormData 对象formData.append('username', '张三');formData.append('email', 'zhangsan@example.com');formData.append('birthDate', 1940);var xhr = new XMLHttpRequest();xhr.open("POST", "/register");xhr.send(formData);// ------// 使用 `HTML` 表单(`Form`)来提交表单> <form id='registration' name='registration' action='/register'>> <input type='text' name='username' value='张三'>> <input type='email' name='email' value='zhangsan@example.com'>> <input type='number' name='birthDate' value='1940'>> <input type='submit' onclick='return sendForm(this.form);'>> </form>
FormData 也可以将现有表单构造生成(基于 HTML 的 Form 元素快速建立 FormData 对象),并可以在此基础上 append 表单数据。
var formElement = document.querySelector("form"); // 获取现有表单对象var formData = new FormData(formElement); // 根据表单对象 new 一个 FormData 对象formData.append('csrf', 'e69a18d7db1286040586e6da1950128c'); // 为 FormDate 对象添加新的表单信息var request = new XMLHttpRequest();request.open("POST", "submitform.php");request.send(formDate); // send FormDate 数据
FormDate 的 append 方法:下面的 name 代表的是表单的元素的 name,第二个参数是实际的值,第三个参数是可选的,通常是文件名。
// FilesformData.append(name, file, filename);// BlobsformData.append(name, blob, filename);// StringsformData.append(name, value);
FormData 对象也能用来模拟 File 控件,进行文件上传。
// HTML<form id="file-form" action="handler.php" method="POST"><input type="file" id="file-select" name="photos[]" multiple/><button type="submit" id="upload-button">上传</button></form>// JS// 获取表单元素内的文件var fileSelect = document.getElementById('file-select');var files = fileSelect.files; // file对象的files属性,返回一个FileList对象,包含了用户选中的文件。var formData = new FormData();for (var i = 0; i < files.length; i++) {var file = files[i];if (!file.type.match('image.*')) {continue;}formData.append('photos[]', file, file.name);}
FormData也可以加入JavaScript生成的文件。
xhr 有一个 load 事件,与 onreadystatechange 事件相似
xhr 有一个 error 属性,该属性一般为一个函数或函数名。用于监听在 xhr 请求发生错误时执行的操作
abort 方法用来终止已经发出的 HTTP 请求。
用于请求的进度,是 xhr 对象的 upload 属性的一个子属性,会不断返回上传的进度。这是 xhr 的新特性。
需要搭配 HTML5 的新特性:<pregress> 元素使用
// HTML<progress min="0" max="100" value="0">0% complete</progress>// JSfunction upload(blobOrFile) {var xhr = new XMLHttpRequest();xhr.open('POST', '/server', true);xhr.onload = function(e) { ... };// Listen to the upload progress. 'e' is upload eventvar progressBar = document.querySelector('progress');xhr.upload.onprogress = function(e) {if (e.lengthComputable) {progressBar.value = (e.loaded / e.total) * 100;progressBar.textContent = progressBar.value; // Fallback for unsupported browsers.}};xhr.send(blobOrFile);}upload(new Blob(['hello world'], {type: 'text/plain'}));
不论最后 xhr 请求是 load、error 还是 abort,都会伴随着一个 loadend 事件(不管请求成功与否),loadend 也一般是一个回调函数或函数名
readyState属性的值发生改变,就会触发readyStateChange事件。
我们可以通过 onReadyStateChange 属性,指定这个事件的回调函数,对不同状态进行不同处理。尤其是当状态变为 4 的时候,表示通信成功,这时回调函数就可以处理服务器传送回来的数据。
几个事件的总结
var xhr = new XMLHttpRequest();xhr.addEventListener("progress", updateProgress);xhr.addEventListener("load", transferComplete);xhr.addEventListener("error", transferFailed);xhr.addEventListener("abort", transferCanceled);xhr.open();function updateProgress (oEvent) {// ...(见 progress )}function transferComplete(evt) {console.log("The transfer is complete.");}function transferFailed(evt) {console.log("An error occurred while transferring the file.");}function transferCanceled(evt) {console.log("The transfer has been canceled by the user.");}
传统 Ajax 只能向当前网页所在的域名发出 HTTP 请求——同域限制。
解决方案:
三个方案各有优缺点,下面分条阐述
在同源域名下架设一个代理服务器来转发,JavaScript 负责把请求发送到代理服务器:
'/proxy?url=http://www.sina.com.cn'
代理服务器再把结果返回,这样就遵守了浏览器的同源策略。这种方式麻烦之处在于需要服务器端额外做开发。
通过Flash插件发送HTTP请求,这种方式可以绕过浏览器的安全限制,但必须安装Flash,并且跟Flash交互。不过Flash用起来麻烦,而且现在用得也越来越少了。
JSONP 支持,只支持 GET 请求,并要求返回 JavaScriptJSONP 就采用在网页中动态插入 script 元素的做法,向服务器请求脚本文件。使用 GET 的方式发送请求,并需要在 URL 后面加上参数 ?callback=foo,则服务器会将 JSON 数据放到回调 foo 函数内做为参数返回。
许多服务器支持 JSONP 指定回调函数的名称,直接将 JSON 数据放入回调函数的参数。
JSONP 作用过程:
- 预先准备好返回数据处理函数
foo(data){...}Ajax请求JSONP
URL加?callback=foo则返回foo(JSONData)URL不加?callback=foo则返回服务器指定默认bar(JSONData)- 将
foo(JSONData)或bar(JSONData)做为行内脚本插入到页面,foo(JSONData)或bar(JSONData)立即执行(因为我们已经预先准备好了 foo 函数)
优点:对数据格式无限制,不像 JSONP 一样必须准备的是 JSON 格式数据,服务器可自定义接收多种请求方式:GET POST OPTIONS PUT(JSONP 只支持 GET 请求),可以得到更详细的错误信息,部署更有针对性的错误处理代码
缺点:需要服务器开放请求许可,决定权仍旧在服务器手里,浏览器需支持 HTML5,在老式浏览器下表现不佳
CORS 的原理其实很简单,就是自动增加一条 HTTP 头信息的查询,询问服务器端,当前请求的域名是否在许可名单之中,以及可以使用哪些 HTTP 动作。
简单跨域请求 的请求头与响应头信息(GET、HEAD 以及 Content-Type 类型
为 application/x-www-form-urlencoded 、multipart/form-data 和 text/plain):
// AJAX Request HeaderOrigin: http://www.example.com// AJAX Response HeaderAccess-Control-Allow-Origin: http://www.example.comAccess-Control-Allow-Methods: POST, GET, OPTIONSAccess-Control-Allow-Headers: X-PINGOTHERAccess-Control-Max-Age: 1728000
含预检的跨域请求 的请求头与响应头信息(PUT、DELETE 以及 其他类型如 application/json 的 POST 请求):
// AJAX Prefilght Request HeaderOPTIONS /resources/post-here/ HTTP/1.1Host: www.google.comOrigin: http://www.example.comAccess-Control-Request-Method: POSTAccess-Control-Request-Headers: X-PINGOTHER// AJAX Response Header for Preflight RequestHTTP/1.1 200 OKAccess-Control-Allow-Origin: http://www.example.comAccess-Control-Allow-Methods: POST, GET, OPTIONSAccess-Control-Allow-Headers: X-PINGOTHERAccess-Control-Max-Age: 1728000// AJAX Request after Passed Preflight
解释:
Access-Control-Allow-Origin 为 * 或域名时说明允许跨域请求Access-Control-Request-Headers 为允许的自定义请求头Access-Control-Max-Age 为预检过期时间,超过此时间需要再次预检
CORS机制默认不发送cookie和HTTP认证信息,除非在Ajax请求中打开withCredentials属性。
var request = new XMLHttpRequest();request.withCredentials = true;同时,服务器返回
HTTP头信息时,也必须打开Access-Control-Allow-Credentials选项。否则,浏览器会忽略服务器返回的回应。Access-Control-Allow-Credentials: true
由于整个过程都是浏览器自动后台完成,不用用户参与,所以对于开发者来说,使用 Ajax 跨域请求与同域请求没有区别,代码完全一样。但是,这需要服务器的支持,所以在使用 CORS 之前,要查看一下所请求的网站是否支持。