【JavaWeb学习】Ajax(Asynchronous JavaScript And XML)

在浏览器中向服务器发送异步请求,最大的优势就是无刷新获取数据。AJAX不是新的编程语言,而是一种将现有的标准组合在一起使用的新方式。
XML可扩展标记语言,被设计用来传输和存储数据。XML和HTML类似,不同的是HTML中都是预定义标签,而XML中没有预定义标签,全都是自定义标签,用来表示一些数据。
Ajax优点 无需刷新页面而与服务器端进行通信;允许根据用户事件来更新部分页面内容。
Ajax缺点 没有浏览历史,不能回退;存在跨域问题(不可以从a.com向b.com发送请求);SEO(搜索引擎优化)不友好(网页中爬虫爬不到)。

1. HTTP

HTTP: 超文本传输协议,详细规定了浏览器和万维网服务器之间互相通信的规则。
请求报文:
请求行 请求类型、路径、HTTP协议版本 请求头 Host、Cookie、Content-type、User-Agent... 空行 请求体 GET请求的请求体为空,POST请求可以不为空 \begin{array}{c|} \text{请求行} & \text{请求类型、路径、HTTP协议版本} \\ \text{请求头} & \text{Host、Cookie、Content-type、User-Agent...} \\ \text{空行}\\ \text{请求体} & \text{GET请求的请求体为空,POST请求可以不为空} \\ \end{array} 请求行请求头空行请求体请求类型、路径、HTTP协议版本HostCookieContent-typeUser-Agent...GET请求的请求体为空,POST请求可以不为空
请求头的格式参数名: 参数值
响应报文:
响应行 HTTP协议版本、响应状态码、响应状态字符串 响应头 Content-type、Content-length、Content-encoding... 空行 响应体 html代码 \begin{array}{c|} \text{响应行} & \text{HTTP协议版本、响应状态码、响应状态字符串} \\ \text{响应头} & \text{Content-type、Content-length、Content-encoding...} \\ \text{空行}\\ \text{响应体} & \text{html代码} \\ \end{array} 响应行响应头空行响应体HTTP协议版本、响应状态码、响应状态字符串Content-typeContent-lengthContent-encoding...html代码
浏览器将响应体的内容提取出来,进行解析。
查看报文:
F12 → \rightarrow 网络 → \rightarrow 刷新页面 → \rightarrow 显示了当前加载网页的过程中所有发送的请求 → \rightarrow 点击第一个请求,关注“标头”、“响应”、“负载”

2. express

在项目文件夹的最上层中打开集成终端,安装expresscnpm install express --save;在要使用express的文件上打开集成终端,npm init --yes;用nodejs启动node 文件名.js

// 1. 引入express
const express = require('express');
// 2. 创建应用对象
const app = express();
// 3. 创建路由规则
app.get('/', (request, response)=>{
    // request 是对请求报文的封装, response 是对响应报文的封装
    // 设置响应
    response.send("Hello, Express");
});
// 4. 监听端口启动服务
app.listen(8000, ()=>{
    console.log("服务已启动,8000端口监听中... ...");
});
3. Ajax基本操作

服务器端 server.js

// 1. 引入express
const express = require('express');
// 2. 创建应用对象
const app = express();
// 3. 创建路由规则
app.get('/server', (request, response)=>{
    // request 是对请求报文的封装, response 是对响应报文的封装
    // 设置响应头:允许跨域
    response.setHeader('Access-Control-Allow-Origin', '*');
    // 设置响应体
    response.send("Hello, Ajax Get");
});
app.post('/server', (request, response)=>{
    // request 是对请求报文的封装, response 是对响应报文的封装
    // 设置响应头:允许跨域
    response.setHeader('Access-Control-Allow-Origin', '*');
    // 设置响应体
    response.send("Hello, Ajax Post");
});
// 4. 监听端口启动服务
app.listen(8000, ()=>{
    console.log("服务已启动,8000端口监听中... ...");
});
3.1. GET
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Ajax Get Request Learn</title>
    <style>
        #result {
            width: 200px;
            height: 100px;
            border: solid 1px #90b;
            margin-top: 20px;
        }
    </style>
</head>
<body>
    <button type="submit">send request</button>
    <div id="result"></div>
    <script>
        const btn = document.getElementsByTagName("button")[0];
		const result = document.getElementById("result");
		btn.onclick = function(){
		    // 1. 创建对象
		    const xhr = new XMLHttpRequest();
		    // 2. 初始化,设置请求方法和url
		    xhr.open("GET", "http://127.0.0.1:8000/server");
		    // 3. 发送
		    xhr.send();
		    // 4. 事件绑定,处理服务端返回的结果
		    xhr.onreadystatechange = function(){
		        // readystate:xhr对象中的属性,表示状态,有 0 1 2 3 4 五个值
		        /*
		            0:初始化
		            1:open 方法调用完毕
		            2:send 方法调用完毕
		            3:服务端返回部分结果
		            4:服务端返回所有结果
		        */ 
		        if(xhr.readyState === 4){ // 判断响应状态码,以2开头都算响应成功
		            if(xhr.status >= 200 && xhr.status < 300){
		                /**
		                 * 处理结果 - 行 头 空行 体
		                 * 状态码: xhr.status
		                 * 状态字符串: xhr.statusText
		                 * 所有响应头: xhr.getAllResponseHeaders()
		                 * 响应体: xhr.response
		                 **/
		                // 设置 result 文本
		                result.innerHTML = xhr.response;
		            }
		        }
		    }
		}
    </script>
    <script type="text/javascript" src="./server.js"></script>
</body>
</html>

url参数设置

  • 在地址后面用问号隔开地址和参数;
  • 参数名=参数值;
  • 多个参数用 & 隔开;
  • 举例:
    http://127.0.0.1:8000/server?a=100&b=200
3.2. POST
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Ajex Post Request Learn</title>
    <style>
        div{
            width: 200px;
            height: 100px;
            border: solid 1px #90b;
            margin-top: 20px;
        }
    </style>
</head>
<body>
    <!-- 鼠标移动到 div 上时,发送请求,并将返回的结果显示在 div 中 -->
    <div id="result"></div>
    <script>
        const result = document.getElementById("result");
        result.addEventListener("mouseover", function(){
            const xhr = new XMLHttpRequest();
            xhr.open('POST', 'http://127.0.0.1:8000/server');
            xhr.send();
            xhr.onreadystatechange = function(){
                if(xhr.readyState === 4){
                    if(xhr.status >= 200 && xhr.status < 300){
                        result.innerHTML = xhr.response;
                    }
                }
            }
        });
    </script>
    <script type="text/javascript" src="./server.js"></script>
</body>
</html>

设置参数:

  • 请求体:向send()函数传参来设置
    参数名=参数值(其实只要是服务器能处理的格式都可以);
    多个参数用&隔开;
    举例:xhr.send(a=100&b=200);
  • 请求头:在open函数后面添加xhr.setRequestHeader(请求头, 请求值);
    自定义请求头 02:35
3.3. 服务端响应 jsonJSON.parse()、JSON.stringify()

按下按键,显示响应体

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Ajax Json Response</title>
    <style>
        div{
            width: 200px;
            height: 100px;
            border: solid 1px #90b;
            margin-top: 20px;
        }
    </style>
</head>
<body>
    <div id="result"></div>
    <script>
        const result = document.getElementById("result");
        window.onkeydown = function(){
            // 1. 发送请求
            const xhr = new XMLHttpRequest();
            // 方式二:自动转换数据
            xhr.responseType = 'json';
            // 2. 初始化
            xhr.open("GET","http://127.0.0.1:8000/json-server");
            // 3. 发送
            xhr.send();
            // 4. 事件绑定
            xhr.onreadystatechange = function(){
                if(xhr.readyState === 4){
                    if(xhr.status >= 200 && xhr.status < 300){
                        // // 方式一:手动数据数据  json字符串 {"name":"yyt"}
                        // let data = JSON.parse(xhr.response);
                        // result.innerHTML = data.name;
                        // 方式二:自动转换数据
                        result.innerHTML = xhr.response.name;
                    }
                }
            };
        };
    </script>
</body>
</html>
// 服务器端
// 1. 引入express
const express = require('express');
// 2. 创建应用对象
const app = express();
// 3. 创建路由规则: 接收任意类型的请求
app.all('/json-server', (request, response)=>{
    // request 是对请求报文的封装, response 是对响应报文的封装
    // 设置响应头:允许跨域
    response.setHeader('Access-Control-Allow-Origin', '*');
    // 设置响应头:允许所有的头
    response.setHeader('Access-Control-Allow-Headers', '*');
    // 响应一个数据
    const data = { name: 'yyt'};
    // 对对象进行字符串转换
    let str = JSON.stringify(data);
    // 设置响应体
    response.send(str);
});
// 4. 监听端口启动服务
app.listen(8000, ()=>{
    console.log("服务已启动,8000端口监听中... ...");
});
3.5. 请求超时与网络异常 timeout 、ontimeout 、onerror
<button type="submit">send request</button>
<div id="result"></div>
<script>
    const result = document.getElementById("result");
    const btn = document.getElementsByTagName("button")[0];
    btn.addEventListener("click", function(){
        const xhr = new XMLHttpRequest();
        // 超时设置: 如果过了 2000 ms都没有收到响应,那么取消这次请求
        xhr.timeout = 2000;
        // 超时回调:进行一个提醒
        xhr.ontimeout = function(){
            alert('The network is abnormal. Try again later.');
        };
        // 网络异常回调
        xhr.onerror = function(){
            alert('The network is abnormal.');
        };
        xhr.open('GET', 'http://127.0.0.1:8000/server');
        xhr.send();
        xhr.onreadystatechange = function(){
            if(xhr.readyState === 4){
                if(xhr.status >= 200 && xhr.status < 300){
                    result.innerHTML = xhr.response;
                }
            }
        }
    });
</script>
3.6. 取消请求 abort()
<body>
    <button type="submit">send request</button>
    <button type="submit">cancel request</button>
    <div id="result"></div>
    <script>
        const result = document.getElementById('result');
        const btns = document.querySelectorAll('button');
        let xhr = null;
        btns[0].onclick = function(){
            xhr = new XMLHttpRequest();
            xhr.open('GET', 'http://127.0.0.1:8000/server');
            xhr.send();
            xhr.onreadystatechange = function(){
                if(xhr.readyState === 4){
                    if(xhr.status >= 200 && xhr.status < 300){
                        result.innerHTML = xhr.response;
                    }
                }
            }
        }
        btns[1].onclick = function(){
            xhr.abort();
        }
    </script>
</body>
3.7. 请求重复发送问题

发送一个请求时检查之前有没有发送过同样的请求,如果有,那么取消之前的请求。

<body>
    <button type="submit">send request</button>
    <div id="result"></div>
    <script>
        const result = document.getElementById('result');
        const btns = document.querySelectorAll('button');
        let xhr = null;
        let ifSending = false;
        btns[0].onclick = function(){
            // 判断之前是否发送过同样的请求
            if(ifSending){
                xhr.abort();
            };

            xhr = new XMLHttpRequest();
            ifSending = true;
            // 超时设置: 如果过了 2000 ms都没有收到响应,那么取消这次请求
            xhr.timeout = 4000;
            // 超时回调:进行一个提醒
            xhr.ontimeout = function(){
                alert('The network is abnormal. Try again later.');
            };
            // 网络异常回调
            xhr.onerror = function(){
                alert('The network is abnormal.');
            };
            xhr.open('GET', 'http://127.0.0.1:8000/server');
            xhr.send();
            xhr.onreadystatechange = function(){
                if(xhr.readyState === 4){
                    ifSending = false;
                    if(xhr.status >= 200 && xhr.status < 300){
                        result.innerHTML = xhr.response;
                    }
                }
            }
        }
    </script>
</body>
3.8. Axios发送Ajax请求

在BootCDN里搜索axios,找到引用路径

<script crossorigin="anonymous" src="https://cdn.bootcdn.net/ajax/libs/axios/0.27.2/axios.js"></script>
<body>
    <button type="submit">GET</button>
    <button type="submit">POST</button>
    <button type="submit">AJAX</button>
    <div id="result"></div>
    <script>
        const result = document.getElementById('result');
        const btns = document.querySelectorAll('button');
        // 配置 baseURL
        axios.defaults.baseURL = 'http://127.0.0.1:8000';
        btns[0].onclick = function(){
            // Get 请求, 设置各种参数
            axios.get('/server',{
                // url 参数
                params:{
                    id:100,
                    idx:7
                },
                // 请求头信息
                headers:{
                    name:'yyt',
                    age:16
                }
            }).then(value => {
                result.innerHTML = value.data + ', get';
                // console.log(value);
            });
        };
        btns[1].onclick = function(){
            // Get 请求, 设置各种参数
            axios.post('/server',
                {
                    // 请求体
                    username:'kirlant',
                    password:'123456'
                },{
                // url 参数
                params:{
                    id:200,
                    idx:9
                },
                // 请求头信息
                headers:{
                    height:158,
                    weight:53
                }
            }).then(value => {
                result.innerHTML = value.data + ', post';
            });
        };
        // 直接使用 axios 函数发送请求
        btns[2].onclick = function(){
            axios({
                method:'post',
                url:'/server',
                // url 参数
                params:{
                    id:300,
                    idx:5
                },
                // 头信息
                headers:{
                    interesting:'animation'
                },
                // 请求体
                data:{
                    username:'kirlant',
                    password:'123456'
                }
            }).then(response => {
                result.innerHTML = response.data + ', axios';
            });
        }
    </script>
</body>
3.9. fetch()发送Ajax请求
<body>
    <button type="submit">Fetch</button>
    <div id="result"></div>
    <script>
        const result = document.getElementById('result');
        const btn = document.querySelector('button');
        // 配置 baseURL
        btn.onclick = function(){
            fetch('http://127.0.0.1:8000/server', {
                method:'POST',
                headers:{
                    name:'yyt'
                },
                body:'username=kirlant&password=123456'
            }).then(response => {
                return response.text();
            }).then(response => {
                result.innerHTML = response;
            });
        };
    </script>
</body>
3.10. 同源策略

同源策略是浏览器的一种安全策略,违背同源策略就是跨域。Ajax默认遵循同源策略。
同源: 协议、域名、端口号必须完全相同。
演示:在浏览器中输入地址http://127.0.0.1:9000/sameOrigin

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Ajax Json Response</title>
    <style>
        div{
            width: 200px;
            height: 100px;
            border: solid 1px #90b;
            margin-top: 20px;
        }
    </style>
</head>
<body>
    <button type="submit">get user data</button>
    <div id="result"></div>
    <script>
        const result = document.getElementById('result');
        const btn = document.querySelector('button');
        btn.onclick = function(){
            const xhr = new XMLHttpRequest();
            // 满足同源策略时,可以简写url
            xhr.open('GET', '/data');
            xhr.send();
            xhr.onreadystatechange = function(){
                if(xhr.readyState === 4){
                    if(xhr.status >= 200 && xhr.status < 300){
                        result.innerHTML = xhr.response;
                    }
                }
            }
        }
    </script>
</body>
</html>
app.get('/sameOrigin',(request, response)=>{
    // 响应一个同源的页面
    response.sendFile(__dirname + '/ajax.html');
});
app.get('/data',(request, response)=>{
    response.send('user data');
});
// 4. 监听端口启动服务
app.listen(9000, ()=>{
    console.log("服务已启动,9000端口监听中... ...");
});
3.10. 跨域解决方案
  • JSONP
    一个非官方的跨域解决方案,只支持get请求。
    网页中有一些标签天生具有跨域能力,比如imglinkiframescript,JSONP就是利用script标签的跨域能力来发送请求的。
    跨域演示:
    示例一 右键open in default browser用本地方法打开html文件,script标签中引用http协议路径的app.js文件。
    示例二 script标签可以发送跨域请求(终于好像明白了一点),只能响应js代码。
    示例三 跨域服务 jsonp-server 的响应结果是一个函数调用(模板字符串,”handle({"name":"lyl"})“),其参数的实参是我们想返回的数据。函数必须提前声明,这样script接收到之后才能调用。
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Jsonp</title>
    <style>
        #result{
            width: 300px;
            height: 100px;
            border: 1px solid rgb(117, 30, 136);
            padding-left: 5px;
            padding-right: 5px;
        }
    </style>
</head>
<body>
    <div id="result"></div>
    <script>
        function handle(data){
            const result = document.getElementById("result");
            result.innerHTML = data.name;
        };
    </script>
    <script src="http://127.0.0.1:5500/Ajax/app.js"></script>
</body>
</html>
<script src="http://127.0.0.1:8000/jsonp-server"></script>

//server.js
// jsonp演示
app.all('/jsonp-server', (request, response)=>{
    // 设置响应体
    response.send("console.log('jsonp-server, all')");
});
// jsonp.css
#result{
    width: 300px;
    height: 100px;
    border: 1px solid rgb(117, 30, 136);
    padding-left: 5px;
    padding-right: 5px;
}
<!-- jsonp.html -->
<body>
    <div id="result"></div>
    <script>
        function handle(data){
        const result = document.getElementById("result");
        result.innerHTML = data.name;
    };
    </script>
    <script src="http://127.0.0.1:8000/jsonp-server"></script>
</body>
// server.js
app.all('/jsonp-server', (request, response)=>{
    // response.send("console.log('jsonp-server, all')");
    const data = {
        name : 'lyl'
    };
    let str = JSON.stringify(data);
    response.end(`handle(${str})`);
});
  • 原生JSONP实现
    输入用户名,然后丧失焦点,丧失焦点时向服务端发送请求,检测用户名是否存在。
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Jsonp</title>
    <style>
        #result{
            width: 300px;
            height: 100px;
        }
    </style>
</head>
<body>
    <div id="result">
        用户名:
        <input type="text" id="username" title="kirlant">
        <p></p>
    </div>
    <script>
        const input = document.querySelector('input');
        const p = document.querySelector('p');
        function handle(data){
            input.style.border = "solid 1px #f00";
            p.innerHTML = data.msg;
        };
        input.onblur = function(){
            // 获取用户的输入值
            let username = this.value;
            // 向服务端发送请求,检测用户名是否存在
            // 1. 创建 script 标签
            const script = document.createElement('script');
            // 2. 设置标签的 src 属性
            script.src = 'http://127.0.0.1:8000/check-username';
            // 3. 将 script 插入到文档中
            document.body.appendChild(script);
        }
    </script>
</body>
</html>
// jsonp演示
app.all('/check-username', (request, response)=>{
    const data = {
        exist: 1,
        msg:'user name has exist, you cannot use it'
    };
    let str = JSON.stringify(data);
    response.end(`handle(${str})`);
});
  • CORS (Cross-Origin Resource Sharing)跨域资源共享
    CORS是官方的跨域解决方案,特点是不需要在客户端做任何特殊操作,完全在服务器中进行处理,支持get和post请求。CORS标准新增了一组HTTP首部字段,允许服务器声明哪些源通过浏览器有权限访问哪些资源。调用方法response.setHeader()
    CORS通过设置一个响应头来告诉浏览器该请求允许跨域,浏览器收到该响应后就会对该响应放行。
app.all('/server', (request, response)=>{
    response.setHeader('Access-Control-Allow-Origin', '*');
    response.send("server, all");
});
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值