视频地址:https://www.imooc.com/learn/250
该博客是这个视频的笔记。感谢姜维_Wayne老师的课程。
1 Ajax概念介绍
1.1 Ajax是什么
AJAX的全称是:Asynchronous JavaScript and XML(异步的JavaScript和XML)
Ajax不是某种编程语言。它是一种在无需重新加载整个网页的情况下能够更新部分网页的技术。
学习使用Ajax需要掌握的基础知识:HTML、JS、CSS。
异步里面一个重要的概念是XMLHttpResquest。简称为XHR或者xhr。这是一个对象。使用该对象可以实现异步请求和局部刷新。
1.2 XMLHttpRequest对象的创建
实例化一个XHR对象。
var request=new XMLHttpRequest();
现在很多的浏览器都支持标准的XHR对象。但是在IE5和IE6这些古老的浏览器中是不支持的。因为那个时候,XHR对象还未定义。
如果要兼容IE5和IE6甚至更早的版本怎么办呢?
var request;
If(window.XMLHttpRequest){
Request=new XMLHttpRequest();//IE7+,Firefox,Chrome,Opera,Safari...
}else {
Request=new ActiveXObject(“Microsoft.XMLHTTP”);//IE6,IE5
}
1.3 HTTP请求
Http是计算机通过网络进行通讯的规则,HTTP是一种无状态的协议。
一个完整的HTTP请求过程,通常有一下7个步骤:
- 建立TCP连接
- Web浏览器向Web服务器发送请求命令
- Web浏览器发送请求头信息
- Web服务器应答
- Web服务器发送应答头信息
- Web服务器向浏览器发送数据
- Web服务器关闭TCP连接
一个HTTP请求一般由四部分组成:
- HTTP请求的方法或者动作,比如GET或者POST。
- 正在请求的URL
- 请求头,包含一些客户端环境信息,身份验证的信息等等。
- 请求体,即请求正文。可以包含客户提交的查询字符串等信息,表单信息等等。
下面是一个HTTP请求的例子:
GET:一般用于信息的获取,它使用URL传递参数。对所发送信息的数量也有限制,一般在2000个字符。
POST:一般用于修改服务器上的资源,对所分送信息的数量没有限制。
GET请求的是幂等的,幂等是指一个请求无论请求多少次,服务器返回的信息都是相同的。这也是为什么说GET方法是安全的。
一个HTTP响应一般由三部分组成:
- 一个数字和文字组成的状态码,用来显示请求是成功还是失败
- 响应头,包含服务器类型、日期时间、内容类型和长度等等。
- 响应体,也就是响应正文。
下面是一个典型的响应的内容:
状态码:
HTTP状态码由3位数字构成,其中首位数字定义了状态码的类型:
1xx:信息类,表示收到Web浏览器请求,正在进一步的处理中。
2xx: 成功,表示用户请求被正确接收,理解和处理。例如:200 OK
3xx: 重定向,表示请求没有成功,客户必须才去进一步的操作。
4xx: 客户端错误,表示客户端提交的请求有错误,例如404 NOT Found。
5xx: 服务器错误,表示服务器不能完成对请求的处理。
1.4 通过一个XMLHttpRequest发送请求
XHR对象有两个方法:
open(method,url,async)
send(string)
method表示请求方法,不区分大小写,一般使用大写。url可以使用相对路径也可以使用绝对路径。async表示方法是同步的还是异步的。如果为true表示是异步的方法。默认是异步的方法。
send(string),当请求方法为GET时,可以不传入参数。
使用的例子:
1.5 使用XMLHttpRequest取得响应
responseText:获得字符串形式的响应数据
responseXML:获得XML形式的响应数据
status和statusText:以数字和文本形式返回HTTP状态码
getAllResponseHeader():获取所有的响应报头。
getResponseHeader():查询响应中的某个字段的值。
readyState属性,这个属性的变化代表着服务器响应的变化
0:请求未初始化,open方法还没有调用。
1:服务器连接已经建立,open已经调用了。
2:请求已经接收,也就是接收到头信息了。
3:请求处理中,也就是接收到响应主体了。
4:请求已经完成,而且响应已经就绪,也就是响应完成了。
比如下面的代码,是一种经常使用的方法。
var request=new XMLHttpRequest();
request.open(“GET”,”get.php”,true);
request.send();
request.onreadystatechange=function(){
If(request.readyState==4 && request.status===200){
//do something,like request.responseText
}
}
2 Ajax使用的简单例子
通过以上的示例,现在总结一下使用Ajax的步骤:
- 实例化一个XHR对象
- 调用XHR对象的open方法,open完成以后可以设置一些头信息,然后调用send方法发送数据。
- 启用readyState方法进行监听,在指定的状态下做一些事情。
2.1 环境搭建(PHP+Dreamweaver+XAMPP)
使用Dreamweaver配置web服务器用于本地测试。
XAMPP,下载链接为:https://www.apachefriends.org/download.html
使用XAMPP启动Apache 服务器。
服务器启动之后,去Dreamweaver新建一个站点。
Site->new site...
新建一个服务器,
新建一个test.php文件,然后打开,在Dreamweaver中编辑,如下
在下边选择在浏览器中浏览。如下:
PHP语法简介
- PHP脚本以<?php开头,以?>结尾
- 默认的文件扩展名是.php
- PHP语句以分号结尾
课程中使用fiddler工具来发起请求,因为我使用的是mac,mac在安装fiddler的时候需要安装mono,很麻烦,所以我直接使用postman或者idea的客户端工具来测试。在idea中打开HTTP client的方法如下:
2.2 服务器端的实现
不加参数的时候,直接访问,返回结果是“参数错误”。
使用idea的http client发起带参数请求的时候,要注意加参数的方法:
如下:
执行完成之后,返回“没有找到员工”。
传入101,返回正常的结果。
后面的演示以使用方法为主,不再截取大量的执行图片。
执行一个POST请求,先设置header的Content-Type为application/x-www-form-urlencoded,不设置请求参数,在requestBody中输入文本:
name=欧阳锋&number=104&sex=男&job=测试经理
如下图:
执行结果:
当然了,因为在php代码中我们并没有实际保存这个信息。
2.3 客户端的实现
首先写一个简单的页面demo.php,代码如下:
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Demo</title>
<style>
body, input, select, button {
font-size: 16px
}
</style>
</head>
<body>
<h1>员工查询</h1>
<label>请输入员工编号:</label>
<input type="text" id="keyword"/>
<button id="search">查询</button>
<p id="searchResult"></p>
<h1>员工创建</h1>
<label>请输入员工姓名:</label>
<input type="text" id="staffName"/>
<br />
<label>请输入员工编号:</label>
<input type="text" id="staffNumber"/>
<br />
<label>请输入员工编号:</label>
<select id="staffSex">
<option>男</option>
<option>女</option>
</select>
<br />
<label>请输入员工职位:</label>
<input type="text" id="staffJob"/>
<br />
<button id="save">保存</button>
<p id="createResult"></p>
</body>
</html>
这个简单的静态页面效果如下:
我们要实现的是,通过点击按钮,通过Ajax异步的方式,发送请求,调用后台的服务方法,将服务器处理的结果显示在页面上。这个显示是局部地去刷新结果内容。
在Safari中打开调试功能,需要现在偏好设置中的“高级”选项卡中勾选“在菜单栏中显示‘开发’菜单”。然后在点击“开发”->”显示页面资源”。
在页面中嵌入js代码:
实现搜索功能的代码:
document.getElementById("search").onclick=function(){
//发送Ajax查询请求并处理
var request=new XMLHttpRequest();
request.open("GET","service.php? number="+document.getElementById("keyword").value);
request.send();
request.onreadystatechange=function(){
if(request.readyState===4){
if(request.status===200){
document.getElementById("searchResult").innerHTML=request.responseText;
}else{
alert("发生错误:"+request.status);
}
}
}
}
实现保存功能的js代码:
document.getElementById("save").onclick=function(){
//发送Ajax查询请求并处理
var request=new XMLHttpRequest();
request.open("POST","service.php");
var data="name="+document.getElementById("staffName").value
+"&number="+document.getElementById("staffNumber").value
+"&sex="+document.getElementById("staffSex").value
+"&job="+document.getElementById("staffJob").value;
request.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
request.send(data);
request.onreadystatechange=function(){
if(request.readyState===4){
if(request.status===200){
document.getElementById("createResult").innerHTML=request.responseText;
}else{
alert("发生错误:"+request.status);
}
}
}
}
注意:一定不要将单词写错。我就是把“POST”写成了”PST”,查了好久才发现问题。
3 jQuery中的Ajax
3.1 JSON的基本概念
JSON就是JavaScript的对象表示法(JavaScript Object Notation).
JSON是存储和交换文本信息的语法,类似于XML。它采用键值对的方式来组织,易于人们阅读和编写,同时也易于机器解析和生成。
JSON是独立于语言的,也就是说不管什么语言,都可以解析json,只需要按照json的规则来就可以。
JSON与XML相比,有以下几个优点:
json的长度比xml格式小很多。
json读写的速度更快。
json可以使用JavaScript内建的方法直接进行解析,转换成JavaScript对象非常方便。
3.2 JSON的语法规则
JSON数据的书写格式是:名称/值对。
名称/值对组合中的名称写在前面(写在双引号中),值对写在后面(同样在双引号中),中间使用冒号隔开。比如”name”:”郭靖”。值对之间使用逗号隔开。
json的值可以是下面的类型:
- 数字(整数或者浮点数),比如123,1.23
- 字符串(在双引号中)
- 逻辑值(true或者false)
- 数组(在方括号中)
- 对象(在花括号中)
- null
比如下面的例子:
{
"staff":[
{"name":"洪七公","age":70},
{"name":"郭靖","age":35},
{"name":"黄蓉","age":30}
]
}
3.3 JSON的解析
可以使用eval和JSON.parse方法来解析JSON数据。
var jsondata='{"staff":[{"name":"洪七公","age":70},{"name":"郭靖","age":35},{"name":"黄蓉","age":30}]}';
var jsonobj=eval('('+jsondata+')');
alert(jsonobj.staff[1].name);
var jsondata='{"staff":[{"name":"洪七公","age":70},{"name":"郭靖","age":35},{"name":"黄蓉","age":30}]}';
var jsonobj=JSON.parse(jsondata);
alert(jsonobj.staff[1].name;
var jsondata='{"staff":[{"name":"洪七公","age":alert(123)},{"name":"郭靖","age":35},{"name":"黄蓉","age":30}]}';
var jsonobj=eval('('+jsondata+')');
alert(jsonobj.staff[0].name);
var jsondata='{"staff":[{"name":"洪七公","age":alert(123)},{"name":"郭靖","age":35},{"name":"黄蓉","age":30}]}';
var jsonobj=JSON.parse(jsondata);
alert(jsonobj.staff[0].age);
通过以上的测试可以看出来,
JSON.parse会检查json的语法。如果不符合json的语法,那么就会抛出一个错误。而eval不会检查json字符串是否合法,并且会首先执行嵌入在json里面的命令去执行。除非你确定json字符串是合法的和安全的,才可以使用eval。所以一般情况下,我们使用JSON.parse方法。
虽然json使用比较方便,但是写起来还是比较令人眼花的。而且,可能会去验证一个json字符串是否合法。可以使用JSON Validator工具进行校验。
这里有一款很好用的json解析、格式化和校验的工具JSONLint。这是一个在线的网站。地址为:jsonlint.com
3.4 使用jQuery的Ajax
现在来改写前面代码的返回值。
首先做一个格式上的约定:
从服务器返回的json信息都有两个属性。一个是标志服务器处理成功还是失败,另一个就是如果成功,返回客户端需要的信息,如果失败,返回相关的错误信息。服务器处理成功还是失败指的是业务逻辑的成功还是失败。比如如果用户的邮箱地址填写错误,这个就是失败了,但是这个业务是被后端成功执行了。
这个约定需要前端和后端同时遵守才可以。
下面对后端的代码进行改造。
首先把头信息的Content-Type设置为application/json
把返回值都改成约定的json格式。
serverjson.php
<?php
//设置页面内容是html编码格式是utf-8
//header("Content-Type: text/plain;charset=utf-8");
header("Content-Type: application/json;charset=utf-8");
//header("Content-Type: text/xml;charset=utf-8");
//header("Content-Type: text/html;charset=utf-8");
//header("Content-Type: application/javascript;charset=utf-8");
//定义一个多维数组,包含员工的信息,每条员工信息为一个数组
$staff = array
(
array("name" => "洪七", "number" => "101", "sex" => "男", "job" => "总经理"),
array("name" => "郭靖", "number" => "102", "sex" => "男", "job" => "开发工程师"),
array("name" => "黄蓉", "number" => "103", "sex" => "女", "job" => "产品经理")
);
//判断如果是get请求,则进行搜索;如果是POST请求,则进行新建
//$_SERVER是一个超全局变量,在一个脚本的全部作用域中都可用,不用使用global关键字
//$_SERVER["REQUEST_METHOD"]返回访问页面使用的请求方法
if ($_SERVER["REQUEST_METHOD"] == "GET") {
search();
} elseif ($_SERVER["REQUEST_METHOD"] == "POST"){
create();
}
//通过员工编号搜索员工
function search(){
//检查是否有员工编号的参数
//isset检测变量是否设置;empty判断值为否为空
//超全局变量 $_GET 和 $_POST 用于收集表单数据
if (!isset($_GET["number"]) || empty($_GET["number"])) {
echo '{"success":false,"msg":"参数错误"}';
return;
}
//函数之外声明的变量拥有 Global 作用域,只能在函数以外进行访问。
//global 关键词用于访问函数内的全局变量
global $staff;
//获取number参数
$number = $_GET["number"];
$result = '{"success":false,"msg":"没有找到员工。"}';
//遍历$staff多维数组,查找key值为number的员工是否存在,如果存在,则修改返回结果
foreach ($staff as $value) {
if ($value["number"] == $number) {
$result = '{"success":true,"msg":"找到员工:员工编号:' . $value["number"] .
',员工姓名:' . $value["name"] .
',员工性别:' . $value["sex"] .
',员工职位:' . $value["job"] . '"}';
break;
}
}
echo $result;
}
//创建员工
function create(){
//判断信息是否填写完全
if (!isset($_POST["name"]) || empty($_POST["name"])
|| !isset($_POST["number"]) || empty($_POST["number"])
|| !isset($_POST["sex"]) || empty($_POST["sex"])
|| !isset($_POST["job"]) || empty($_POST["job"])) {
echo '{"success":false,"msg":"参数错误,员工信息填写不全"}';
return;
}
//TODO: 获取POST表单数据并保存到数据库
//提示保存成功
echo '{"success":true,"msg":"员工:' . $_POST["name"] . ' 信息保存成功!"}';
}
?>
通常,我们不会使用原生的XHR来实现Ajax请求。我们经常使用第三方的js库,比如jQuery。
用jQuery来实现Ajax。
语法:
jQuery.ajax([settings])
- type:类型,请求的方法。比如POST或者GET,默认为GET。
- url:发送请求的地址
- data:是一个对象,连同请求发送到服务的数据。
- dataType: 预期服务器返回的数据类型。如果不指定,jQuery将自动根据HTTP包MIME信息来智能判断,一般我们采用json格式,可以设置为“json”.
- success:是一个方法,请求成功后的回调函数。传入返回后的数据,以及包含成功代码的字符串。
- error:是一个方法,请求失败时调用此函数。传入XMLHttpRequest对象。
现在使用jQuery改写刚才的例子。
首先需要引入jQuery。可以存放在本地,也可以引用网络上的jQuery。比如引用百度的资源。http://apps.bdimg.com/libs/jquery/1.11.1/jquery.js
修改demo.html中的<script>标签中的内容。把原来使用原生的方法删掉。替换部分的代码如下:
<script src="http://apps.bdimg.com/libs/jquery/1.11.1/jquery.js"></script>
<script>
$(document).ready(function() {
$("#search").click(function(){
$.ajax({
type:GET,
url:"service.php?number="+$("#keyword").val(),
dataType:"json",
success: function(data){
//jquery的惯例,传参数就是设置值。不传参数就是获取值。
if(data.success){
$("#searchResult").html(data.msg);
}else{
$("#searchResult").html("出现错误:"+data.msg);
}
},
error: function(jqXHR){
alert("发生错误:"+jqXHR.status);
}
});
});
$("#save").click(function(){
$.ajax({
type:POST,
url:"service.php",
dataType:"json",
data:{
name:$("#staffName").val(),
number:$("#staffNumber").val(),
sex:$("#staffSex").val(),
job:$("#staffJob").val(),
},
success: function(data){
//jquery的惯例,传参数就是设置值。不传参数就是获取值。
if(data.success){
$("#createResult").html(data.msg);
}else{
$("#createResult").html("出现错误:"+data.msg);
}
},
error: function(jqXHR){
alert("发生错误:"+jqXHR.status);
}
});
});
});
</script>
4 跨域
4.1 什么是跨域
一个域名地址的组成:
- 当协议、子域名、主域名、端口号中任意一个不相同的时候,都算作不同的域。
- 不同的域之间相互请求资源,就算作”跨域“。
当出现跨域请求的时候,会报类似于如下的错误。
JavaScript处于安全方面的考虑,不予许跨域调用其他页面的对象。简单地理解就是,因为JavaScript同源策略的限制。a.com域名下的js无法操作b.com或者c.a.com域名下的对象。
4.2 跨域的处理方法
跨域的处理方法一,使用代理
通过在同域名的web服务器端创建一个代理:
比如在北京的web服务器的后台
www.beijing.com/proxy-shanghaiservice.php
来调用上海服务器
的服务,然后再把响应结果返回给前端,这样前端调用北京同域名的服务就和调用上海的服务效果相同了。
处理跨域的方法二:使用JSONP
JSONP可以用来解决主流浏览器GET方法的跨域数据访问的问题。
在www.aaa.com的页面中:
<script>
function jsonp(json){
Alert(json[“name”]);
}
</script>
<script src=”http://www.bbb.com/jsonp.js”></script>
在www.bbb.com的页面中:
jsonp({‘name’:’洪七公’,’age’:24});
处理跨域的方法三:使用XHR2
HTML5提供的XMLHttpRequest Level2已经实现了跨域访问以及其他的一些新功能
IE10以下的版本都不支持
在服务端做一些小小的改造即可:
header(‘Access-Control-Allow-Origin:*’);
header(‘Access-Control-Allow-Methods:POST,GET’);