@yangfch3
2016-10-24T00:37:55.000000Z
字数 12706
阅读 5361
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);
// 也可以是 POST
xmlhttp.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
onreadystatechange
readyState
: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, Safari
xmlhttp=new XMLHttpRequest();
}else{// code for IE6, IE5
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange=cfunc;
xmlhttp.open("GET",url,true);
xmlhttp.send();
}
// 抽象出来两个常变参数:url cfunc
function 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=-1
dim 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 then
hint=""
for i=1 to 30
if q=ucase(mid(a(i),1,len(q))) then
if hint="" then
hint=a(i)
else
hint=hint & " , " & a(i)
end if
end if
next
end if
'如果未找到提示,则输出返回 "no suggestion"
'否则输出正确的值
if hint="" then
response.write("no suggestion")
else
response.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.EOF
for each x in rs.Fields
response.write("<tr><td><b>" & x.name & "</b></td>")
response.write("<td>" & x.value & "</td></tr>")
next
rs.MoveNext
loop
response.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
属性表示的是状态码,一个数字,例如:200
statusText
属性表示的是状态提示(包括状态码和说明),是一个字符串,例如:"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
,第二个参数是实际的值,第三个参数是可选的,通常是文件名。
// Files
formData.append(name, file, filename);
// Blobs
formData.append(name, blob, filename);
// Strings
formData.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>
// JS
function upload(blobOrFile) {
var xhr = new XMLHttpRequest();
xhr.open('POST', '/server', true);
xhr.onload = function(e) { ... };
// Listen to the upload progress. 'e' is upload event
var 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
请求,并要求返回 JavaScript
JSONP
就采用在网页中动态插入 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 Header
Origin: http://www.example.com
// AJAX Response Header
Access-Control-Allow-Origin: http://www.example.com
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER
Access-Control-Max-Age: 1728000
含预检的跨域请求 的请求头与响应头信息(PUT
、DELETE
以及 其他类型如 application/json
的 POST
请求):
// AJAX Prefilght Request Header
OPTIONS /resources/post-here/ HTTP/1.1
Host: www.google.com
Origin: http://www.example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER
// AJAX Response Header for Preflight Request
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://www.example.com
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER
Access-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
之前,要查看一下所请求的网站是否支持。