1. 什么是AJAX?
1.1 XML
XML(可扩展标记语言),被设计用来传输和存储数据。和HTML区别是,HTML中都是预定义标签XML是自定义标签
<student>
<name>悟空</name>
<age>100</age>
<gender>男</gender>
</student>
1.2 ajax概述
AJAX 并不是编程语言。
AJAX 是一种从网页访问 Web 服务器的技术。
AJAX是异步的JavaScript和XML(Asynchronous JavaScript And XML),现在XML格式已经被Json格式所取代。简单点说,就是使用 XMLHttpRequest
对象与服务器通信。 它可以使用JSON,XML,HTML和text文本等格式发送和接收数据。AJAX最吸引人的就是它的“异步”特性,也就是说它可以在不重新刷新页面的情况下与服务器通信,交换数据,或更新页面。
Ajax的优点
- 在不重新加载页面的情况下发送请求给服务器。
- 允许根据用户事件来更新部分页面内容
Ajax的缺点
- 没有浏览历史,不能回退
- 存在跨域问题(同源策略)
- seo不友好
1.3 ajax 如何工作
- 网页中发生一个事件(页面加载、按钮点击)
- 由 JavaScript 创建 XMLHttpRequest 对象
- XMLHttpRequest 对象向 web 服务器发送请求
- 服务器处理该请求
- 服务器将响应发送回网页
- 由 JavaScript 读取响应
- 由 JavaScript 执行正确的动作(比如更新页面)
1.4 http
HTTP协议(HyperText Transfer Protocol,超文本传输协议)是因特网上应用最为广泛的一种网络传输协议,所有的WWW文件都必须遵守这个标准。HTTP是一个基于TCP/IP通信协议来传递数据(HTML 文件, 图片文件, 查询结果等)。
三种请求方式
-
XHR(Axios和Jquery都是对XHR的封装)
- Axios
- Jquery
fetch
http
请求报文
- 行
- GET /s?wd=ajax%20w3school&rsv_spt=1
- HTTP/1.1
- 头
- Host:baodu.com
- Cookie:name=tom
- Content-type:application/x-www-form-urlencoded
- User-Agent:chrome 83
- 空行
- 体
- username=admin&password=admin
响应报文
-
行
- HTTP/1.1 200 OK
-
头
- Content-Type:text/html;charset-utf-8
- Content-length:2048
- Content-encoding:gzip
空行
-
体
<html> <head></head> <body></body> </html>
1.5 状态码
当客户端向服务器发送请求时,服务器会向客户端返回一个HTTP状态码,表明该请求的处理结果。HTTP状态码是3个数字组成的代码,其中第一个数字代表响应类型,后面两个数字不代表任何意义,只是用于进一步细分提示信息。
以下是完整的HTTP状态码及其含义:
1xx(信息性状态码):
- 100 Continue:服务器已经接收到客户端发送的部分请求,并且请求需要客户端继续操作。
- 101 Switching Protocols:客户端请求服务端切换协议。
2xx(成功状态码):
- 200 OK:请求成功。
- 201 Created:资源已被创建。
- 202 Accepted:请求已被接受,但服务器尚未执行任何操作。
- 203 Non-authoritative Information:服务器已成功处理请求,但返回的信息可能来自另一来源。
- 204 No Content:服务器处理成功,但是没有任何资源可以返回给客户端。
- 205 Reset Content:服务器要求客户端重置内容,例如清除表单。
- 206 Partial Content:服务器已成功处理请求,但只返回了部分资源。
3xx(重定向状态码):
- 300 Multiple Choices:有多个资源可供选择。
- 301 Moved Permanently:请求的资源永久移动到新位置,客户端应该使用新地址重新访问。
- 302 Found:请求的资源临时移动到了新位置,客户端应该继续请求原始地址。
- 303 See Other:当请求特定网页时,服务器返回的响应指向其他的网页,客户端应该使用GET方法获取新的资源。
- 304 Not Modified:客户端缓存的资源是最新的,服务器告诉客户端可以直接使用缓存的资源。
- 307 Temporary Redirect:请求的资源临时移动到新位置,客户端应该继续请求原始地址。
4xx(客户端错误状态码):
- 400 Bad Request:请求无效。
- 401 Unauthorized:请求需要身份验证。
- 402 Payment Required:保留,表示未来可能使用。
- 403 Forbidden:请求被拒绝。
- 404 Not Found:请求的资源不存在。
- 405 Method Not Allowed:请求方法不被允许。
- 406 Not Acceptable:请求头中的Accept字段不被服务器支持。
- 407 Proxy Authentication Required:请求要求代理身份验证。
- 408 Request Timeout:服务器等待请求时间过长。
- 409 Conflict:请求的资源存在冲突。
- 410 Gone:请求的资源已不存在。
- 411 Length Required:缺少Content-Length请求头。
- 412 Precondition Failed:请求头中设置的条件无法满足。
- 413 Payload Too Large:请求的payload过大。
- 414 URI Too Long:请求的URI过长。
- 415 Unsupported Media Type:请求头中的Content-Type不被服务器支持。
- 416 Range Not Satisfiable:请求范围不符合要求。
- 417 Expectation Failed:服务器无法满足Expect头中指定的期望条件。
5xx(服务器错误状态码):
- 500 Internal Server Error:服务器收到了一个无法处理的请求。
- 501 Not Implemented:请求方法不被服务器支持。
- 502 Bad Gateway:作为网关或代理工作的服务器尝试执行请求时,从上游服务器接收到无效响应。
- 503 Service Unavailable:服务器处于维护或过载状态,无法处理请求。
- 504 Gateway Timeout:作为网关或代理工作的服务器无法在规定时间内从上游服务器获取响应。
- 505 HTTP Version Not Supported:服务器不支持HTTP版本。
2. 基本使用
express
安装
npm i express
启动
node server.js
//demo/server.js(在demo目录运行)
demo/server.js
//1.引入express
const express = require('express');
//2.创建应用对象
const app = express();
//3.创建路由规则
app.get('/server',(request,response)=>{
response.setHeader('Access-Control-Allow-Origin','*')
response.send('Hellow Express');
})
app.post('/server',(request,response)=>{
response.setHeader('Access-Control-Allow-Origin','*')
response.setHeader('Access-Control-Allow-Headers','*')
response.send('Hellow Express');
})
app.all('/server',(request,response)=>{
response.setHeader('Access-Control-Allow-Origin','*')
response.setHeader('Access-Control-Allow-Headers','*')
response.send('Hellow Express');
})
app.listen(8000,()=>{
console.log('服务器已经启动,8000端口监听中...');
})
基本请求
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script>
function Send() {
console.log('dddd')
// 1.创建一个xhr对象
const xhr = new XMLHttpRequest();
// 2.打开文件或请求
//get请求参数加在路径后面http://127.0.0.1:8080/server?name=tom&age=12
xhr.open('GET', 'http://127.0.0.1:8080/server')
// 3. 设置头信息
xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
// 4.发生数据
xhr.send()
// 5 监听事件,xhr的readyState(0,1,2,3,4)发生变化
//* 0 (未初始化) or (**请求还未初始化**)
//* 1 (正在加载) or (**已建立****服务器链接**)
//* 2 (加载成功) or (**请求已接受**)
//* 3 (交互) or (**正在处理请求**)
//* 4 (完成) or (**请求已完成并且响应已准备好**)
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200) {
console.log("xhr", xhr);
console.log("响应数据", xhr.responseText);
document.body.append(xhr.responseText);
}
}
}
</script>
</head>
<body>
<button onclick="Send()">点击发送请求</button>
</body>
</html>
post
<script>
function Send() {
console.log('dddd')
// 1.创建一个xhr对象
const xhr = new XMLHttpRequest();
// 2.打开文件或请求
//get请求参数加在路径后面http://127.0.0.1:8080/server?name=tom&age=12
xhr.open('POST', 'http://127.0.0.1:8080/server')
// 3. 设置头信息
xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
xhr.setRequestHeader('name','tom');
// 4.发送数据到服务器
xhr.send("name=tom&age=12");
// 5 监听事件,xhr的readyState(0,1,2,3,4)发生变化
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if(xhr.status >= 200&& xhr.status <= 300)
console.log("xhr", xhr);
console.log("响应数据", xhr.responseText);
document.body.append(xhr.responseText);
}
}
}
</script>
返回json数据
//1.引入express
const express = require('express');
//2.创建应用对象
const app = express();
//3.创建路由规则
app.get('/server',(request,response)=>{
response.setHeader('Access-Control-Allow-Origin','*')
response.send('Hellow Express');
})
app.post('/server',(request,response)=>{
response.setHeader('Access-Control-Allow-Origin','*')
response.setHeader('Access-Control-Allow-Headers','*')
response.send('Hellow Express');
})
app.all('/json-server',(request,response)=>{
response.setHeader('Access-Control-Allow-Origin','*')
response.setHeader('Access-Control-Allow-Headers','*')
const data = {
name:'tom',
age:12
}
//json转字符串
let str = JSON.stringify(data)
//send()里面必须是字符串
response.send(str);
})
app.listen(8000,()=>{
console.log('服务器已经启动,8000端口监听中...');
})
html
字符串转json
- 1、
xhr.responseType='json'
- 2、
console.log("响应数据",JSON.parse(xhr.response));
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script>
function Send() {
console.log('已点击,开始请求...')
// 1.创建一个xhr对象
const xhr = new XMLHttpRequest();
//xhr.responseType='json'
// 2.打开文件或请求
//get请求参数加在路径后面http://127.0.0.1:8080/server?name=tom&age=12
xhr.open('get', 'http://127.0.0.1:8000/json-server')
// 3. 设置头信息
//xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
// 4.发送数据到服务器
xhr.send();
// 5 监听事件,xhr的readyState(0,1,2,3,4)发生变化
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if(xhr.status >= 200&& xhr.status <= 300)
console.log("xhr", xhr);
console.log("响应数据",JSON.parse(xhr.response));
document.body.append(xhr.responseText);
}
}
}
</script>
</head>
<body>
<button onclick="Send()">点击发送请求</button>
<div id="result"></div>
</body>
</html>
nodemon自动重启
安装
npm i nodemon -g
使用
nodemon server.js
其他
网络异常
-
xhr.timeout = 2000
两秒无返回则取消请求 - 超时回调
xhr.ontimeout = function(){
alert('网络异常请稍后重试')
}
- 网络异常回调
xhr.onerror = function(){
alert('你的网络出现了问题')
}
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script>
function Send() {
console.log('已点击,开始请求...')
const xhr = new XMLHttpRequest();
xhr.timeout = 2000
xhr.ontimeout = function(){
alert('网络异常请稍后重试')
}
xhr.onerror = function(){
alert('你的网络出现了问题')
}
xhr.open('get', 'http://127.0.0.1:8000/delay')
xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
xhr.send();
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if(xhr.status >= 200&& xhr.status <= 300)
console.log("xhr", xhr);
console.log("响应数据",xhr.response);
document.body.append(xhr.responseText);
}
}
}
</script>
</head>
<body>
<button onclick="Send()">点击发送请求</button>
<div id="result"></div>
</body>
</html>
手动取消ajax请求
<script>
let x = null
function onClick1(){
x = new XMLHttpRequest()
x.open('GET','http://127.0.0.1:8000')
x.send()
}
function onClick2(){
x.abort();
}
</script>
重复请求
<script>
let x = null
let isSending = false //是否正在发送AJAX请求
function onClick1(){
if(isSending){
x.abort()
}
x = new XMLHttpRequest()
isSending = true
x.open('GET','http://127.0.0.1:8000')
x.send()
x.onreadystatechange = function(){
if(x.readyState===4){
isSending = false
}
}
}
function onClick2(){
x.abort();
}
</script>
jquery发送ajax请求
<body>
<button id="button">点击发送GET请求</button>
<button id="button2">点击发送POST请求</button>
<button id="button2">点击发送通用请求</button>
<script>
$('#button').click(function () {
console.log('sss')
$.get('http://127.0.0.1:8000/server', { a: 100, b: 200 }, function (data) {
console.log(data)
},'json')
})
$('#button2').click(function () {
console.log('sss')
$.post('http://127.0.0.1:8000/server', { a: 100, b: 200 }, function (data) {
console.log(data)
})
})
$('#button3').click(function () {
$.ajax({
url: '',
data: { a: 1, b: 2 },
type: 'GET',
dataType:'json',
success: function (data) {
console.log(data)
},
timeout:2000,
error:function(){
console.log('出错了')
},
headers:{
name:'tom'
}
})
})
</script>
</body>
axios
get
const axios = require('axios');
// 向给定ID的用户发起请求
axios.get('/user?ID=12345')
.then(function (response) {
// 处理成功情况
console.log(response);
})
.catch(function (error) {
// 处理错误情况
console.log(error);
})
.then(function () {
// 总是会执行
});
// 上述请求也可以按以下方式完成(可选)
axios.get('/user', {
params: {
ID: 12345
},
headers:{
}
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
})
.then(function () {
// 总是会执行
});
// 支持async/await用法
async function getUser() {
try {
const response = await axios.get('/user?ID=12345');
console.log(response);
} catch (error) {
console.error(error);
}
}
post
axios.post('/user', {firstName: 'Fred',lastName: 'Flintstone'},{
params:{
id:10
},
header:{
name:'tom'
}
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
发起多个并发请求
function getUserAccount() {
return axios.get('/user/12345');
}
function getUserPermissions() {
return axios.get('/user/12345/permissions');
}
Promise.all([getUserAccount(), getUserPermissions()])
.then(function (results) {
const acct = results[0];
const perm = results[1];
});
通用方法
axios({
method:'POST'
url:'',
params:{
id:1
},
headers:{
a:1
},
data:{
user:''
}
}).then(response=>{
console.log(response)
})
5. fetch 请求
getData = ()=>{
const {keywordElement:{value:keyword}} = this
fetch('/api1/search/user?q=${keyword}').then(
response=>{
console.log('联系服务器成功了')
//PubSub.publish('demo',{isloading:false,users:response.data.item})
return response.json()
},
error => {
console.log('联系服务器失败了',error)
return new Promise(()=>())
//PubSub.publish('demo',{isloading:true,err:error.message})
}
).then(
response => {console.log('获取数据成功了',response)}
error => {console.log('获取数据失败了',error)}
)
}
优化
getData = ()=>{
const {keywordElement:{value:keyword}} = this
fetch('/api1/search/user?q=${keyword}').then(
response=>{
console.log('联系服务器成功了')
return response.json()
},
).then(
response => {console.log('获取数据成功了',response)}
).catch(
error => {console.log('请求出错',error)}
)
}
}
async await再优化
getData = async()=>{
const {keywordElement:{value:keyword}} = this
try{
const response = await fetch('/api1/search/user?q=${keyword}')
const data = await response.json()
console.log(data)
}catch(error){
console.log('请求出错',error)
}
}
跨域解决方案
同源策略
同源策略(Same-origin policy)是一种Web安全策略,它为浏览器的JavaScript代码定义了一种安全机制,以确保Web文档或脚本只能与产生它们的源相互交互,而与其他源的文档或脚本不能进行交互。
同源指的是==协议(Protocol)==、==域名(Domain)==和==端口号(Port)==都相同。当浏览器向一个服务器发起请求时,会根据这三个要素构成一个唯一的“源”,并根据同源策略来限制脚本、Cookie或其他资源的访问。
同源策略的作用是防止恶意脚本访问与自己来源不一致的敏感信息。通过同源策略,可以保证用户数据的私密性、完整性和可靠性,避免跨站点脚本攻击(XSS)、跨站点请求伪造(CSRF)等安全问题。
需要注意的是,同源策略只对客户端代码起作用,而对于服务器端之间的通信则没有限制。因此,在设计网络应用程序时,需要注意对服务器端接口进行安全性检查和认证。
JSONP
JSONP(JSON with Padding)是一种跨域数据交互的技术,它利用了script标签的src属性不受同源策略限制的特点,通过动态创建script标签,将需要获取的数据作为参数传递给服务器,服务器返回一段JavaScript代码,该代码会在客户端执行,从而实现跨域获取数据。
==JSONP只能处理GET请求==,因为JSONP的原理是利用script标签的src属性可以跨域访问的特性,将需要获取的数据作为参数传递给服务器,服务器返回的数据会被封装在回调函数中,客户端通过定义该函数,从而获取到数据。
原生
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!-- <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.js"></script> -->
</head>
<body>
用户名:<input type="text" id="username" />
<p></p>
<script>
const input = document.querySelector('input')
const p = document.querySelector('p')
function handle(data) {
p.style.border = 'solid 1px #000'
p.innerHTML = data.msg
}
input.onblur = function () {
//获取用户输入
let username = this.value
//1 创建script标签
const script = document.createElement('script')
//2 设置script的 src 属性
script.src = 'http://127.0.0.1:8000/check-username'
//将script标签插入到文档中
document.body.appendChild(script)
}
</script>
</body>
</html>
server.js
//1.引入express
const express = require('express');
//2.创建应用对象
const app = express();
//3.创建路由规则
app.all('/check-username', (request, response) => {
const data = {
exist: 1,
msg: '用户名已经存在'
};
let str = JSON.stringify(data);
//前端script的src引入后,服务端可拿到handle()
response.end(`handle(${str})`);
})
app.listen(8000, () => {
console.log('服务器已经启动,8000端口监听中...');
})
jquery版
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.js"></script>
</head>
<body>
<button>发送请求</button>
<div id="result"></div>
const input = document.querySelector('input')
const p = document.querySelector('p')
function handle(data) {
p.style.border = 'solid 1px #000'
p.innerHTML = data.msg
}
input.onblur = function () {
//获取用户输入
let username = this.value
//1 创建script标签
const script = document.createElement('script')
//2 设置script的 src 属性
script.src = 'http://127.0.0.1:8000/check-username'
//将script标签插入到文档中
document.body.appendChild(script)
}
</script> -->
<script>
$('button').eq(0).click(function () {
$.getJSON('http://127.0.0.1:8000/check-username?callback=?', function (data) {
console.log(data)
$('#result').html(
data.msg
)
})
})
</script>
</body>
</html>
server
//1.引入express
const express = require('express');
//2.创建应用对象
const app = express();
//3.创建路由规则
app.all('/check-username', (request, response) => {
const data = {
exist: 1,
msg: '用户名已经存在'
};
let str = JSON.stringify(data);
let cb = request.query.callback
//前端script的src引入后,服务端可拿到handle()
response.end(`${cb}(${str})`);
})
app.listen(8000, () => {
console.log('服务器已经启动,8000端口监听中...');
})
CORS
CORS(Cross-Origin Resource Sharing)是一种跨域资源共享的机制,它允许浏览器向跨域服务器发出XMLHttpRequest请求,从而实现跨域访问。CORS需要浏览器和服务器的支持,可以通过设置Access-Control-Allow-Origin等响应头来实现跨域访问。
在服务端添加请求头
app.get('/server',(request,response)=>{
response.setHeader('Access-Control-Allow-Origin','*')
response.setHeader('Access-Control-Allow-Origin','http://127.0.0.1/server')
response.setHeader('Access-Control-Allow-Headers','*')
response.setHeader('Access-Control-Allow-Method','*')
response.send('Hellow Express');
})
app.post('/server',(request,response)=>{
response.setHeader('Access-Control-Allow-Origin','*')
response.setHeader('Access-Control-Allow-Headers','*')
response.send('Hellow Express');
})