文章目录
1、Ajax简介
什么是 AJAX :
- AJAX 是一种用于创建快速动态网页的技术。
- 通过在后台与服务器进行少量数据交换,AJAX 可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。
- 传统的网页(不使用 AJAX)如果需要更新内容,必需重载整个网页面。
- 有很多使用 AJAX 的应用程序案例:新浪微博、Google 地图、开心网等等。
AJAX工作原理 :
2、get请求与post请求
- http请求方式有get、post、put、delete等多种,最常用的就是get和post;
- GET请求也可传参到后台,但是其参数在浏览器的地址栏的url中可见,所以隐私性安全性较差,且参数长度也是有限制的
- POST请求传递参数放在Request body中,不会在url中显示,比GET要安全,且参数长度无限制
- 对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);
- 而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。
- 也就是说,GET只需要汽车跑一趟就把货送到了,而POST得跑两趟,第一趟,先去和服务器打个招呼“嗨,我等下要送一批货来,你们打开门迎接我”,然后再回头把货送过去。
- 因为POST需要两步,时间上消耗的要多一点,看起来GET比POST更有效。因此Yahoo团队有推荐用GET替换POST来优化网站性能。但这是一个坑!跳入需谨慎。为什么?
1、 GET与POST都有自己的语义,不能随便混用。
2、据研究,在网络环境好的情况下,发一次包的时间和发两次包的时间差别基本可以无视。而在网络环境差的情况下,两次包的TCP在验证数据包完整性上,有非常大的优点。
3、并不是所有浏览器都会在POST中发送两次包,Firefox就只发送一次。
3、原生Ajax请求后台
由于 http 请求方式的不同,Ajax的请求分为get请求和post请求;
原生Ajax之get请求
创建 XMLHttpRequest 对象
var xmlhttp = new XMLHttpRequest();
向服务器发送请求
- 如需将请求发送到服务器,我们使用 XMLHttpRequest 对象的 open() 和 send() 方法:
xmlhttp.open("GET","ajax_info.txt",true);
xmlhttp.send();
- 在上面的例子中,你可能得到的是缓存的结果。为了避免这种情况,可以向 URL 添加一个唯一的 ID:
xmlhttp.open("GET","/try/ajax/demo_get.php?t=" + Math.random(),true);
xmlhttp.send();
- 如果你希望通过 GET 方法发送信息,可以向 URL 添加信息:
xmlhttp.open("GET","/try/ajax/demo_get2.php?fname=Henry&lname=Ford",true);
xmlhttp.send();
等待响应
- 当请求被发送到服务器时,我们需要执行一些基于响应的任务。每当 readyState 改变时,就会触发 onreadystatechange 事件。readyState 属性存有 XMLHttpRequest 的状态信息。
代码示例:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<button type="button" id="btn">发送请求</button>
</body>
<script type="text/javascript">
document.getElementById("btn").onclick = function() {
alert("进来了吗?");
//1、创建对象
var xmlhttp = new XMLHttpRequest();
//2、发送请求
xmlhttp.open("GET", "http://wthrcdn.etouch.cn/weather_mini?city=西安", true);
xmlhttp.send();
//3、等待响应
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
var resultText = xmlhttp.responseText;
console.log(resultText);
}
}
}
</script>
</html>
以上代码使用的是一个天气预报的接口,该接口的后台代码进行了CORS处理(后面介绍),因此我们能顺利的访问到信息;有的接口就会报出缺少请求头的错误,这是由于跨域请求引出的问题;(后面介绍)
原生Ajax之post请求
一个简单的post请求
xmlhttp.open("POST","/try/ajax/demo_post.php",true);
xmlhttp.send();
- 如果需要像 HTML 表单那样 POST 数据,可以使用
setRequestHeader()
来添加HTTP 头
。然后在 send() 方法中规定希望发送的数据:
xmlhttp.open("POST","/try/ajax/demo_post2.php",true);
xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");
xmlhttp.send("fname=Henry&lname=Ford");
代码示例:
后台代码
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet(value = "/login", name = "LoginServlet")
public class LoginServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("请求来了");
//获取前台的请求参数
String username = request.getParameter("username");
String password = request.getParameter("password");
System.out.println(username);
System.out.println(password);
//调用JDBC 去查询数据库……
//给前台响应数据
//设置返回数据的格式以及编码
response.setContentType("application/json;charset=utf-8");
//{"msg":"ok"}
// \用于转义
String json="{\"msg\":\"ok\"}";
//设置响应头 告诉浏览器,不管来自哪里的请求,我认为都可以,你浏览器,不要拦截我响应给人家的数据
response.setHeader("Access-Control-Allow-Origin","*");
response.getWriter().write(json);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
前台代码
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<form action="#" method="post" id="myForm">
用户名:
<input type="text" id="uname" name="username" value=""/>
<br>
密码:
<input type="text" id="upwd" name="password" value=""/>
<br>
<input type="submit" value="提交"/>
<br>
</form>
<div id="mydiv">
</div>
</body>
<script type="text/javascript">
var xmlhttp;
document.getElementById("myForm").onsubmit = function (event) {
//阻止表单默认的提交方式,使用post方式的异步提交
event.preventDefault();
//我们下来进行异步提交
//创建Ajax对象
xmlhttp = new XMLHttpRequest();
//post请求的请求参数,不能拼接在地址栏后面
//http://localhost:8080/login 绝对路径
//自己服务器里面的前台页面,请求自己服务器里面的后台Servlet 不需要写绝对路径
xmlhttp.open("POST", "/login", true); //相对路径
//3.设置请求头信息
xmlhttp.setRequestHeader('content-type', 'application/x-www-form-urlencoded');
//发送请求post请求的参数传递给
var uname = document.getElementById("uname").value;
var upwd = document.getElementById("upwd").value;
xmlhttp.send(`username=${uname}&password=${upwd}`);
xmlhttp.onreadystatechange = function () {
//200 响应状态码 200表示后台成功响应你了
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
//接收后台响应的字符串
var jsonStr = xmlhttp.responseText;
//解析json字符串
var jsonObj = JSON.parse(jsonStr);
console.log(jsonStr);
if (jsonObj.msg == 'ok') {
document.getElementById("mydiv").innerText=jsonObj.msg;
}
}
}
}
</script>
</html>
运行结果
4、jQuery封装后的Ajax请求后台
$.ajax()
——执行异步的Ajax请求
- 语法:
$.ajax({键值对});
//使用$.ajax()发送异步请求
代码示例:
$.ajax({
url: "ajaxServlet1111", // 请求路径
type: "POST", //请求方式
data: {
"username": "jack",
"age": 23
},
success: function(data) {
alert(data);
}, //响应成功后的回调函数
error: function() {
alert("出错啦...")
}, //表示如果请求响应出现错误,会执行的回调函数
dataType: "text" //设置接受到的响应数据的格式
});
请求一个笑话接口:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="js/jquery-3.3.1.js" type="text/javascript" charset="utf-8"></script>
</head>
<body>
<button type="button">请求笑话</button>
<button type="button">请求笑话使用get()</button>
</body>
<script type="text/javascript">
$('button').eq(0).click(function() {
//采用jQuery发送Ajax请求
var ajaxData = {
method: "GET",
url: "https://autumnfish.cn/api/joke/list",
data: {
num: 1
},
success: function(resData) {
//jQuery已经把后台返回的JSON字符串转为了JSON对象了
console.log(resData);
console.log(resData.msg);
},
error: function(error) {
alert("请求失败!");
},
dataType: "json",
//设置接收到的响应数据的格式
}
//发送Ajax请求
$.ajax(ajaxData);
});
</script>
</html>
$.get()
——发送get请求
- 语法:
$.get(URL,data,function(data,status,xhr),dataType)
请求一个笑话接口:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="js/jquery-3.3.1.js" type="text/javascript" charset="utf-8"></script>
</head>
<body>
<button type="button">请求笑话</button>
<button type="button">请求笑话使用get()</button>
</body>
<script type="text/javascript">
$('button').eq(1).click(function() {
$.get({
url: "https://autumnfish.cn/api/joke/list",
data: {
num: 1
},
//请求成功的处理:resData后台响应的数据
success: function(resData) {
console.log(resData);
console.log(resData.msg);
},
//设置接受到响应数据的格式
dataType: "json"
});
});
</script>
</html>
$.post()
——发送post请求
- 语法:
$(selector).post(URL,data,function(data,status,xhr),dataType)
jQuery封装下的Ajax之post请求
后台代码与之前原生的post请求时是一样的,这里给出jQuery封装的post请求代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="js/jquery-3.3.1.js" type="text/javascript" charset="utf-8"></script>
</head>
<body>
<form action="#" method="post" id="myForm">
用户名:
<input type="text" id="uname" name="username" value="" />
<br>
密码:
<input type="text" id="upwd" name="password" value="" />
<br>
<input type="submit" value="提交" />
<br>
</form>
<div id="mydiv">
</div>
</body>
<script type="text/javascript">
$('#myForm').submit(function() {
var uname = document.getElementById("uname").value;
var upwd = document.getElementById("upwd").value;
$.post({
url: "/login",
data: {
username: uname,
password: upwd
},
//请求成功的处理:resData后台响应的数据
success: function(resData) {
console.log(resData);
console.log(resData.msg);
if (resData.msg == 'ok') {
document.getElementById("mydiv").innerText = resData.msg;
}
},
//设置接受到响应数据的格式
dataType: "json"
});
});
</script>
</html>
$.getJSON()
——直接请求JSON数据,post,get请求都可以
$(selector).getJSON(url,data,success(data,status,xhr))
5、跨域请求问题
什么是跨域
- 现代浏览器为了安全,做了一个同源限制,也就是所谓的同源安全策略。本质上,其实是不存在所谓的跨不跨域的;把浏览器想象成一个发送网络请求的软件。按照道理来说,请求都是可以发送出去的。但是在 web 端,浏览器里,这么做的就不合适。如果网络上的接口可以不受限制的被任意人调用,那将是一个非常混乱的场景。所以,为了防止这种情况,浏览器做了这个同源策略来防止这种情况发生;一般服务器不管这个事情;
- 跨域指的是,A网站内部向B网站发送一个AJAX请求,由于浏览器有同源策略的限制,默认情况下,是不允许 A网站向 B 网站请求数据资源的。
http://my.website.com/ ---> http://your.website.com/ 是不允许的
- 具体来说,凡是发送请求中,协议,主机名,端口号有一个不同,即为跨域请求。
- 上述例子:
my.website.com 和 your.website.com
就属于主机名不同而导致的跨域。
- 也就是:协议相同 + 域名相同 + 端口号相同,浏览器才认为是同一个网站,才不同受到同源策略的影响,才可以正常的发送 AJAX 请求。其他情况下,发送 AJAX 请求,都属于跨域。
请注意: 这里说的是 XMLHttpRequest 下的 AJAX 请求,对于
<img> , <script>, <link>
等标签,就不存在跨域请求。(除非对方后台做了防盗链)
- 所谓的同源策略是浏览器实现的,而和后台服务器无关;
- A 发送请求到 B,请求实际上是发送到了 B 后台, B后台接受到数据后,其实也有返回。
只不过,这个数据返回到浏览器之后,浏览器把这个数据给劫持了,不让A网站使用。
为什么要跨域
- 既然跨域这么麻烦,都不能从网站后台拿到数据,那为什么还要搞跨域呢?
- 因为当一个项目变大时,把所有的内容都丢在一个网站或者是后台服务器中是不现实的。
比如一个网站体量很大。有很多可以独立且复杂的业务;
比如有一个订单管理的后台数据API网站服务。
比如有一个用户管理的后台数据API网站服务。
比如有一个新闻管理的后台数据API网站服务。
最后剩下的就是web的UI层面的东西,把这个UI层面的东西和哪个数据服务API的网站集成在一起比较合适呢?都不适合,它应该是一个专门的网站。
- 所以域名可能存在如下情况:
主网站(UIWeb)域名为: http://www。relax。com/
订单数据服务接口域名为: https://order。relax。com/orderList
用户数据服务接口域名为: https://users。relax。com/userList
新闻数据服务就扣域名为: http://news。relax。com/newList ;
所以,UIWeb 如果需要请求这数据。
请求订单数据: 协议不同 https|http , 二级域名不同 order | www , 端口号不同 443|80 --> 跨域
请求用户数据: 协议不同 https|http , 二级域名不同 users | www , 端口号不同 443|80 --> 跨域
请求新闻数据列表: 协议相同 http|http , 二级域名不同 order | www , 端口号相同 80|80 – 跨域
不管请求哪个后台服务器的数据,都存在跨域的情况,也就是无法利用 ajax 异步的获取到数据内容。
利用JSONP进行跨域
- jsonp 跨域是里用在浏览器中,下载一个script标签是不受同源策略限制的特性,它只支持get请求;
原理: jsonp 跨域是里用在浏览器中,下载一个script标签是不受同源策略限制的特性,比如我们会在有网络连接的情况下,从bootcdn中引入js文件,而不是本地引入;
- 利用 jsonp 进行跨域的步骤:
- 客户端动态的创建一个 script 标签
- 设置 script 标签的src 为跨域的服务器后台.
- src 中需要带一个 callback 查询字符串,告诉后台,前端提供的方法名是什么;
- 然后后端直接返回一串
callback(data)
的字符串给浏览器即可; - 前端端口是:8080
使用jQuery代码动态创建script标签,并设置src属性:
var scriptStr = $("<script src='https:\/\/tcc.taobao.com\/cc\/json\/mobile_tel_segment.htm?tel=" + telNum +
"&callback=getJsonData' charset='utf-8'><\/script>");
$('body').append(scriptStr);
function getJsonData(data) {
console.log(data);
}
注意:
1、写在$()
里面的代码一定要记得转义,因为我踩过坑!!
2、通过将前端方法当作参数传递给服务器端,然后服务器端注入参数之后再返回,实现服务器端向客户端通信;
3、后台的原理是:
- 首先在客户端注册一个callback
- 然后把callback的名字传给服务器
- 此时,服务器生成json数据
- 然后以javascript语法的方式,生成function,function名字就是传递上来带参数jsonp
- 最后将json数据直接以入参的方式,放置function中,这样就生成js语法的文档
- 返回给客户端
- 客户端浏览器,解析script变迁,并执行返回javascript文档
- 此时数据作为参数,传入了客户端预先定义好的callback函数里。
- 简单的说,就是利用script标签没有跨域限制的“漏洞”来达到与第三方通讯的目的,还有一些标签比如:link、img标签也不会受到同源策略的影响!
4、callback就是指定回调函数名,这是大家约定俗成的键,如果是你自己写的后台,你可以随意规定这个键,回调函数名由自己随意制定;后台拿到回调函数名,将数据从数据库当中取出来,放在函数名()中,前台将这段字符串识别为JS代码,解析渲染;
- jsonp使用总结:
1、利用 script 标签异步加载,而非传统的ajax;
2、异步 script 加载不受浏览器同源策略限制;
3、给 script.src=callback=fn来告知请求的后台前端提供的fn是什么;
4、基本就是前端提供方法,后端提供数据并调用前端的这个方法;
5、jsonp 只支持 get 请求(本质上不就是下载文件吗?下载文件一般都是get请求)
利用CORS进行跨域
- cors 是
Cross-Origin-Resource-Sharing
的缩写,也是现代浏览器普遍支持的一种非常方便的跨域方案。 - 它的具体工作流程是:
浏览器在检测到你发送的 ajax 请求不是同源请求时,会自动在 http 的头部添加一个 origin 字段;
-
之前也说过,请求怎么滴都能发送出去,我们拿不到数据是因为浏览器在中间做了一层劫持。
-
获取不到数据的原因也很简单:
1、这是一次跨域请求。
2、请求确实发送到服务器了。
3、服务器也把数据返回到了浏览器。
4、但是服务器返回的响应头里,没有告诉浏览器哪个域名可以访问这些数据(也就是没有设置Access-Control-Allow-Origin
)
5、于是浏览器就把这个数据丢弃了。我们也就无法获取到这个数据。
6、这个时候,只需要后台在相应头里加上一个Access-Control-Allow-Origin:*
即可完成跨域数据获取。
- CORS 跨域方案简单总结:
1、当浏览器发送一个请求是跨域的时候,会自动加上 Origin
2、需要后台配合设置响应头 Access-Control-Allow-Origin:*|Origin即可完成跨域。
3、CORS 支持 GET,POST 常规请求。
4、同时也支持 PUT,DELETE等非 post,get的请求,但会先发一次预检请求。
利用proxy模式进行跨域
- 核心思想是:
让前端请求我们自己的后台,自己的后台再去跨域请求真实的数据(因为后台之间的访问不涉及同源策略的问题),然后把数据返回给前台;
实现方式:可以利用 nginx 做反向代理,以及我们自己写一个后台中转的服务器。
6、使用jQuery封装后的Ajax实现天气预报查询
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>天知道</title>
<link rel="stylesheet" href="css/reset.css" />
<link rel="stylesheet" href="css/index.css" />
<script src="js/jquery-3.3.1.js" type="text/javascript" charset="utf-8"></script>
</head>
<body>
<div class="wrap" id="app">
<div class="search_form">
<div class="logo"><img src="img/logo.png" alt="logo" /></div>
<div class="form_group">
<input type="text" id="city" class="input_txt" placeholder="请输入查询的天气" />
<button class="input_sub" id="btn">
搜 索
</button>
</div>
<div class="hotkey">
<a style="cursor: pointer;">北京</a>
<a style="cursor: pointer;">上海</a>
<a style="cursor: pointer;">广州</a>
<a style="cursor: pointer;">深圳</a>
</div>
</div>
<ul class="weather_list" id="myUL">
</ul>
</div>
</body>
</html>
<script type="text/javascript">
$("#btn").click(function() {
var cityName = $("#city").val().trim();
// alert(cityName);
//1、获取输入框内的城市名称
if (!cityName) {
alert("请输入一个城市");
return;
}
//调用get请求方法
$.get({
url: "http://wthrcdn.etouch.cn/weather_mini",
data: {
city: cityName
},
//请求成功的处理:resData后台响应的数据
success: function(resData) {
// console.log(resData.data.forecast);
// console.log(resData.data.yesterday);
var weathArr = resData.data.forecast;
var yesterdayData = resData.data.yesterday;
showData(weathArr, yesterdayData);
},
//设置接受到响应数据的格式
dataType: "json"
});
});
$("[style]").click(function() {
var cityName = $(this).text();
// alert(typeof cityName);
//调用get请求方法
$.get({
url: "http://wthrcdn.etouch.cn/weather_mini",
data: {
city: cityName
},
//请求成功的处理:resData后台响应的数据
success: function(resData) {
var weathArr = resData.data.forecast;
var yesterdayData = resData.data.yesterday;
showData(weathArr, yesterdayData);
},
//设置接受到响应数据的格式
dataType: "json"
});
});
function showData(weathArr, yesterdayData) {
var myUL = $("#myUL");
var liStr = "";
liStr +=
`<li>
<div class="info_type"><span class="iconfont">${yesterdayData.type}</span></div>
<div class="info_temp">
<b>${yesterdayData.low}</b>
~
<b>${yesterdayData.low}</b>
</div>
<div class="info_date"><span>${yesterdayData.date}</span></div>
</li>`;
for (var i = 0; i < weathArr.length; i++) {
var json = weathArr[i];
liStr +=
`<li>
<div class="info_type"><span class="iconfont">${json.type}</span></div>
<div class="info_temp">
<b>${json.low}</b>
~
<b>${json.low}</b>
</div>
<div class="info_date"><span>${json.date}</span></div>
</li>`;
}
myUL.html(liStr);
// myUL.get(0).innerHTML = liStr;
}
</script>
7、使用JSONP实现手机号码信息查询
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="js/jquery-3.3.1.js" type="text/javascript" charset="utf-8"></script>
<style type="text/css">
#top {
justify-content: center;
}
input {
width: 350px;
height: 30px;
border-top-color: #000000;
}
button {
height: 35px;
width: 80px;
margin-left: -6px;
background-color: mediumseagreen;
border: 0px;
color: whitesmoke;
border: 1px black solid;
border-radius: 3px;
}
table {
width: 600px;
height: 300px;
margin-top: 20px;
text-align: center;
}
</style>
</head>
<body>
<!-- 记得校验手机号对不对 -->
<center>
<h1>手机号码归属地专业在线查询</h1>
<div id="top">
<div style="margin-left: -24%;">
手机号码(段):
</div>
<div style="margin-left: ;margin-top: 8px;">
<input type="text" id="telNum" value="" placeholder="请输入手机号码(段)" name="tel" />
<button type="button">查询</button>
</div>
</div>
<br>
</center>
</body>
<script type="text/javascript">
function getJsonData(data) {
var strTable =
`<table border="1" cellspacing="0" cellpadding="">
<tr>
<th colspan="2">查询结果</th>
</tr>
<tr>
<td>您查询的手机号码段</td>
<td>${data.telString}</td>
</tr>
<tr>
<td>卡号归属地</td>
<td>${data.province}</td>
</tr>
<tr>
<td>卡类型</td>
<td>${data.catName}</td>
</tr>
<tr>
<td>区号</td>
<td>${data.areaVid}</td>
</tr>
<tr>
<td>邮编</td>
<td>${data.ispVid}</td>
</tr>
<tr>
<td colspan="2"><a href="#">更多详情的邮编区号</a></td>
</tr>
</table>`;
$('#top').append(strTable);
}
//获取button按钮,绑定点击事件
$("button").click(function() {
//如果已经查询过一次,页面上存在表格,清空该表格
if ($('#top').has('table').length) {
// alert("进来没");
$('table').remove();
}
//获取表单输入
var telNum = $("#telNum").val().trim();
var myreg = /^[1][3,4,5,7,8,9][0-9]{9}$/;
if (!myreg.test(telNum)) {
alert('手机号格式不正确,请重新输入!');
$("#telNum").val(' ');
//让input重新获得光标
$("#telNum").focus();
} else {
alert("手机号输入正确!");
// alert(telNum);
//动态创建script标签
var scriptStr = $("<script src='https:\/\/tcc.taobao.com\/cc\/json\/mobile_tel_segment.htm?tel=" + telNum +
"&callback=getJsonData' charset='utf-8'><\/script>");
$('body').append(scriptStr);
}
});
</script>
</html>