使用 JavaScript 与 jQuery 两种方式搭配 PHP,来各别实作 AJAX 应用,并解说 AJAX 的原理,与 HTTP request、response 的 headers、body 内容解说,以及实现 AJAX 的 XMLHttpRequest 对象。
简介
什么是 AJAX
AJAX(Asynchronous JavaScript and XML,异步的 JavaScript 与 XML 技术)并不是一种编程,而是一种不需重新加载整个网页的情况下,能够更新部分网页,综合了「伺服」(如 PHP)与「客户」(JavaScript)语言的浏览器端网页开发技术。
表單(form)vs AJAX
表单 – 发送 HTTP 请求
早期传统的Web应用,通常都是使用表单(form)向服务器发送一个 HTTP 请求,服务器接收处理传来的表单并送回一个新的网页,但前后两个页面的大部份 HTML 码通常仅有约 5% 是变动的资料,这种做法浪费了许多带宽。
想要更新内容或者提交一个表单,必须重新加载整个网页
AJAX – 发送 HTTP 请求
AJAX 应用可以仅向服务器传送并取回指定资料,接着在客户端使用 JavaScript 处理服务器响应的资料,因只取须要的资料,所以服务器回应更快且负荷也减少了。
使用 JavaScript 实时与服务器进行少量资料交换,来异步局部更新网页
AJAX 运作原理
用户在浏览器触发一个事件,例如点击按钮
将上述获的事件的同时,使用 JavaScript 的对象,在背景对「Web 服务器」发送一个 HTTP 请求,达到与「Web 服务器」进行数据的异步交换XMLHttpRequest
将从「Web 服务器」取得的资料,使用 JavaScript 操作 DOM,来实现动态局部更新「浏览器」的网页内容
HTTP
完整的 HTTP 流程
一个完整的 HTTP 流程,通常有下面七个步骤:
建立 TCP 连接
浏览器:向「Web 服务器」发送请求命令
浏览器:发送请求表头(headers)
Web 服务器:回應(response)
Web 服务器:发送回应(response)信息
Web 服务器:向浏览器发送资料
Web 服务器:关闭 TCP 连接
HTTP 請求方法(GET 與 POST)
获取
适用于信息获取(查询结果)
使用 URL 传递参数(name=value)
对所发送信息的数量有限制,一般在2,000个字串
传递参数(name=value)都在 URL 中,任何人都可见
发布
适用于修改服务器上的资料
所有传递的参数(name=value)将被嵌入 HTTP 的请求主体(body)
对所发送信息的数量无限制
传递参数(name=value)不可见
HTTP 請求(request)
一个 HTTP 请求由四个部分构成:
HTTP 请求方法(method)或「动词」(verb),比如是 GET 或 POST 请求
要请求的 URL,就是请求的网址
一组选择性的请求表头(headers),包含一些「客户端环境」与「身份验证」信息...
一个选择性的请求主体(body),也就是请求正文,可包含客户提交的「查询字符串」或「表单」信息...
HTTP 请求内容
请求方法:GET
请求地址:/service.php?number=1020501
HTTP 版本:HTTP/1.1
请求表头:红框部分
请求主体:绿框部分
请求「表头」与「主体」之间有一个空行,用来表示请求「表头」已经结束了,接下来是请求「主体」
GET/service.php?number=1020501 HTTP/1.1
Host: localhost
Connection: keep-alive
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.99 Safari/537.36
q=0.01
Accept: */*
Referer: http://localhost/javascript-demo.html
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-TW,zh;q=0.8,en-US;q=0.6,en;q=0.4number=1020501
HTTP 回应(response)
服务器的 HTTP 响应(response)有三个部分:
由数值与文字所组成的状态码(status code),指示请求成功或失败
一组响应表头(headers)包含许多有用的信息,例如服务器类型、日期时间、内容类型和长度...
回应主体(body)也就是回应正文,例如“文字字符串”或“HTML 代码”...
http 回应内容
HTTP 版本:HTTP/1.1
回应状态码:200 OK,代表请求成功
回应数据类型:application/json; charset=UTF-8,代表数据类型 JSON; 编码格式 UTF-8
回应表头:红框部分
回应主体:绿框部分(这里回应的资料格式为 JSON)
回应「表头」与「主体」之间有一个空行,用来表示回应「表头」已经结束了,接下来是回应「主体」
HTTP/1.1 200 OK
Date: Sat, 26 Sep 2015 06:12:28 GMT
Server: Apache/2.4.12 (Win32) OpenSSL/1.0.1l PHP/5.6.8
X-Powered-By: PHP/5.6.8
Content-Length: 111
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: application/json; charset=UTF-8{"number":"1020501","name":"\u738b\u4e00\u5091","sex":"\u7537"}
HTTP 状态码(status code)
HTTP 状态码由 3 位数字组成,第一个位数定义了回应的对象(后两位数没有代表任何具体的分类)。
1xx:指示信息。 表示收到 Web 浏览器请求,正在进一步的处理中
2xx:成功。 表示请求已经被正确接收,并已经被理解和接受,例如:200 OK
3xx:重新导向。 表示请求没有成功,必须采取进一步的动作以完成请求
4xx:客户端错误。 表示客户端提交的请求中有错误或者不能被完成,例如:404 NOT found,代表请求中引用的档案不存在
5xx:服务器错误。 表示服务器不能完成请求的处理,尽管请求是正确的,例如:500 Internal Server Error,代表服务器遇到了一个未曾预料的状况,通常是服务器的代码出错
XMLHttpRequest 物件(object)
浏览器将它们的 HTTP API 定义在一个 对象之上。 此对象的每个实体皆代表一组 request / response 的配对,而这个对象的「属性」与「方法」让你能够:XMLHttpRequest
做出POST、HEAD及普通GET请求(request)的能力
同步(sync)或异步(async)提取 Web 服务器的响应(response),并且能够以「文字」或一个「DOM」文件... 的形式返回内容
XMLHttpRequest 它并不限于只能用在 XML 文件,它可以接收任何形式的文字档案
构造子(constructor)
XMLHttpRequest()
呼叫任何方法前一定要先调用构造子,例如:
var request =XMLHttpRequest();
JavaScript复制
浏览器支持
XMLHttpRequest 对象第一次的出现是在 Microsoft 的 IE5 中,但在 IE5 及 IE6 中它是以 对象的形式提供。 现在标准的 构建子在IE7之前并没有支持(现代浏览器都有支持),但可这样模拟它的功能,以兼容所有浏览器(包含IE7之前):ActiveXXMLHttpRequest()
// 建立 Ajax 物件
request =newajaxRequest();functionajaxRequest(){try{// (除 IE7 之前)支援所有現代瀏覽器var request =newXMLHttpRequest();}catch(e1){try{// (支援 IE6)如果有的話就用 ActiveX 物件的最新版本
request =newActiveXObject("Msxml2.XMLHTTP.6.0");}catch(e2){try{// (支援 IE5)否則就用較舊的版本
request =newActiveXObject("Msxml2.XMLHTTP.3.0");}catch(e3){// 不支援 Ajax,拋出錯誤thrownewError("XMLHttpRequest is not supported");}}}return request;}
JavaScript复制
属性(attribute)
就绪状态
HTTP 请求的状态,当一个 初次建立时,这个属性的值为 0,直到接收到完整的 HTTP 回应(response),则值增加到 4。XMLHttpRequest
JavaScript XMLHttpRequest readyState 值
值 | 状态 | 描述 |
0 | 未发送 | open() 尚未被呼叫 |
1 | OPEND | open() 方法已被呼叫,但 方法未被呼叫send() |
2 | HEADERS_RECEIVED | send() 方法已被呼叫,而且可取得 header 与状态 |
3 | 装载 | 回应数据下载中,此时会拥有部分资料responseText |
4 | 做 | 回应(response)已经完成 |
响应文本
响应请求的字符串资料; 如果请求失败或尚未完成,则为 。null
响应XML
响应请求的 DOM 文档对象(Document Object),如果请求失败、尚未完成或回传数据无法解析为 XML 或 HTML 文件,则为 。 回应会如同 text / html 一般一样被解析。 当 被设为 “document” 而且请求不是异步,回应会如同 text / html 一般一样被解析。nullresponseType
地位
请求回应状态,也就是 HTTP 状态码(status code),例如:200 代表成功; 404 NOT Found,代表请求中引用的档案不存在。
状态文本
与 不一样的是回应状态字串(Status String),且会包含完整的回传讯息,例如:“200 OK”; "404 NOT Found"。status
事件(event)
就绪状态更改
当 属性每次改变时呼叫的 JavaScript 函数,是从用户界面的线程中呼叫。readyState
方法(method)
中止()
终止送出的请求。
这个方法会将属性重置为 0,回应不再必要的时候,可以呼叫这个方法。readyState
getAllResponseHeaders()
以字符串类型返回所有响应表头(response headers),如果没有( 小于 3)则返回 。readyStatenull
请注意,对于多重请求,这个方法会回传目前请求的回应而非最早的回应。
DOMString getAllResponseHeaders();
JavaScript复制
getResponseHeader()
以字符串类型返回指定表头(header),如果没有( 小于 3)或请求尚未发送回传 。readyStatenull
DOMString getResponseHeader(DOMString header);
JavaScript复制
JavaScript XMLHttpRequest getResponseHeader() 参数
页眉 | 回传指定表头,例如设定 “Content-Type” ,则返回 application/json; charset=UTF-8、text/plain; charset=UTF-8... 对应的数据类型 |
打开()
初始化 HTTP 请求。
voidopen(
DOMString method,
DOMString url,
optional boolean async,
optional DOMString user,
optional DOMString password
);
JavaScript复制
JavaScript XMLHttpRequest open() 参数
方法 | HTTP 方法,如 GET、POST、PUT 和 DELETE…… |
网址 | 请求目的 URL |
异步 | (非必填)默认为 ,代表是否要送出异步请求。 如为 , 方法于收到回应前不会回传; 如为 ,透过事件处理器通知请求完成,当在多重请求下,此值必须为 ,否则会导致例外错误truefalsesend()truetrue |
用户 | (非必填)默认为空字符串,代表验证用户名 |
密码 | (非必填)默认为空字符串,代表验证用密码 |
发送()
发送 HTTP 请求(request),对于异步(async)请求会立即回传,同步(sync)请求则会等到请求完成才回传。
voidopen(
DOMString method,
DOMString url,
optional boolean async,
optional DOMString user,
optional DOMString password
);
JavaScript复制
setRequestHeader()
设定 HTTP 请求表头(header),必须要在呼叫后,且在 前呼叫。open()send()
使用 POST 方法必须设定 HTTP 请求表头(header),GET 方法则不须设定
voidsetRequestHeader(
DOMString header,
DOMString value
);
JavaScript复制
JavaScript XMLHttpRequest setRequestHeader() 参数
页眉 | 表头名称(header name),例如设定 Content-Type |
价值 | 表头值(header value),例如设定 application/x-www-form-urlencoded |
范例
程式
以下将使用 JavaScript 与 jQuery 两种方式搭配 PHP,来各别实作可查询、新建员工的 AJAX 应用。
示例使用 JSON 作为「资料交换格式」,可参考 JSON 格式与 JavaScript 解析教学范例
JavaScript 与 jQuery 所使用的 PHP 代码都相同
伺服器端 PHP – service.php:
<?php// 設置資料類型 json,編碼格式 utf-8header('Content-Type: application/json; charset=UTF-8');// 定義一個二維陣列來儲存員工資料,每筆員工資料為一個陣列$staff=array(array('number'=>'1020501','name'=>"王一傑",'sex'=>'男'),array('number'=>'1020502','name'=>"王二傑",'sex'=>'男'),array('number'=>'1020503','name'=>"王三傑",'sex'=>'男'));// 判斷如果是 GET 請求,則進行搜尋;如果是 POST 請求,則進行新建// $_SERVER['REQUEST_METHOD'] 返回訪問頁面使用的請求方法if($_SERVER['REQUEST_METHOD']=="GET"){search($staff);}elseif($_SERVER['REQUEST_METHOD']=="POST"){create();}// 通過員工編號搜尋functionsearch($staff){// 檢查是否有員工編號的參數// isset() 方法檢測變數是否設置;empty() 方法判斷值是否為空// 超全域變數 $_GET 和 $_POST 用於收集表單資料if(!isset($_GET['number'])||empty($_GET['number'])){echojson_encode(array('msg'=>'沒有輸入員工編號!'));return;}// 搜尋員工編號for($i=0,$len=count($staff);$i<$len;$i++){// 如果存在,儲存對應的陣列if($staff[$i]['number']==$_GET['number']){$result=$staff[$i];}}// 將陣列編碼成 JSON 字串echoisset($result)?json_encode($result):json_encode(array('msg'=>'沒有該員工!'));}// 新建員工functioncreate(){// 如果員工資料未填寫完全if(!isset($_POST['number'])||empty($_POST['number'])||!isset($_POST['name'])||empty($_POST['name'])||!isset($_POST['sex'])||empty($_POST['sex'])){echojson_encode(array('msg'=>'員工資料未填寫完全!'));return;}// 可將獲取的 POST 表單資料,儲存到資料庫(該部分未實作)// 儲存成功,返回員工姓名echojson_encode(array('name'=>$_POST['name']));}
.PHP复制
客户端 JavaScript – javascript-demo.html:
<!DOCTYPE html><html><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"><title>JavaScript Ajax Demo</title><style type="text/css">
input, button, select {
margin-bottom: 10px;}</style></head><body><h1>查詢員工</h1><label for="keyword">請輸入員工編號:</label><input type="text" id="keyword"><button id="search">查詢</button><p id="searchResult"></p><h1>新建員工</h1><label for="staffNumber">請輸入員工編號:</label><input type="text" id="staffNumber"><br><label for="staffName">請輸入員工姓名:</label><input type="text" id="staffName"><br><label for="staffSex">請輸入員工性別:</label><select id="staffSex"><option value="男">男</option><option value="女">女</option></select><br><button id="save">保存</button><p id="createResult"></p><script type="text/JavaScript">
document.getElementById("search").onclick=function(){// 發送 Ajax 查詢請求並處理var request =newXMLHttpRequest();
request.open("GET","service.php?number="+ document.getElementById("keyword").value);
request.send();
request.onreadystatechange=function(){// 伺服器請求完成if(request.readyState ===4){// 伺服器回應成功if(request.status ===200){var type = request.getResponseHeader("Content-Type");// 取得回應類型// 判斷回應類型,這裡使用 JSONif(type.indexOf("application/json")===0){var data =JSON.parse(request.responseText);if(data.number){
document.getElementById("searchResult").innerHTML ='[找到員工] 員工編號:'+data.number +', 姓名:'+
data.name +', 性別:'+ data.sex;}else{
document.getElementById("searchResult").innerHTML = data.msg;}}}else{alert("發生錯誤: "+ request.status);}}}}
document.getElementById("save").onclick=function(){// 發送 Ajax 查詢請求並處理var request =newXMLHttpRequest();
request.open("POST","service.php");// POST 參數須使用 send() 發送var data ="name="+ document.getElementById("staffName").value +"&number="+ document.getElementById("staffNumber").value +"&sex="+ document.getElementById("staffSex").value;// POST 請求必須設置表頭在 open() 下面,send() 上面
request.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
request.send(data);
request.onreadystatechange=function(){// 伺服器請求完成if(request.readyState ===4){// 伺服器回應成功if(request.status ===200){var type = request.getResponseHeader("Content-Type");// 取得回應類型// 判斷回應類型,這裡使用 JSONif(type.indexOf("application/json")===0){var data =JSON.parse(request.responseText);if(data.name){
document.getElementById("createResult").innerHTML ='員工:'+ data.name +',儲存成功!';}else{
document.getElementById("createResult").innerHTML = data.msg;}}}else{alert("發生錯誤"+ request.status);}}}}</script></body></html>
JavaScript复制
客户端 jQuery – jquery-demo.html:
<!DOCTYPE html><html><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"><title>jQuery Ajax Demo</title><style type="text/css">
input, button, select {
margin-bottom: 10px;}</style></head><body><h1>查詢員工</h1><label for="keyword">請輸入員工編號:</label><input type="text" id="keyword"><button id="search">查詢</button>