16-Django与Ajax交互

楔子

Ajax的优点

  1. 可以无需刷新页面与服务器进行通信
  2. 允许你根据用户事件来更新部分页面内容

Ajax的缺点

  1. 没有浏览历史(不能回退)
  2. 存在跨域问题(同源)
    1. 可以解决
  3. SEO不友好,对爬虫页不友好
    1. 异步加载

前后端传输数据的编码格式

前后端传输数据的编码格式主要有三种

urlencoded
formdata
json

回顾HTTP相关知识

# HTTP(Hypertext Transport Portocol)协议【超文本传输协议】,协议详细规定了浏览器和万维网服务器之间互相通信的规则。

# 请求报文
行    请求方法 / url路径 / HTTP协议版本 
			POST /?ie=utf-u  HTTP/1.1
头    Host:...
      Cookie:...
      Content-type:....
       ...
空行
体  如果是get请求,请求体是空的,如果是post请求,请求体可以不为空
	username=admin&password=123

# 响应报文
行		协议版本  / 响应状态码   / 响应字符串
		* 200 OK * 404 找不到  * 403 被禁止 * 401 未授权 * 500 服务器内部错误
		# 状态字符串跟状态码是对应的,不需要我们单独去设置
		eg: HTTP/1.1 200 OK
头		和请求头差不多
空行
体		一般是一一个HTML文件

image-20240309165039233

image-20240309165219721

image-20240309165250567

为什么使用Ajax

img

使用express框架作为服务端

express官网:https://expressjs.com/zh-cn/

node中文官网:https://nodejs.cn/download/

  1. 先安装node.js
  2. 执行命令npm init --yes初始化
  3. 安装express
    1. npm install express --save
  4. 启动服务,终端运行node xxx.js即可

一个简单的express服务器写法

// 1. 引入express
const express = require('express');

// 2. 创建应用对象
const app = express();

// 3. 创建路由规则
// request是对请求报文的一个封装
// response是对响应报文的封装
app.get('/', (request, response)=>{
    // 设置响应
    response.send('hello express.');
});

// 4. 监听端口,启动服务  8000代表的就是端口,后面是一个函数
app.listen(8000, ()=>{
    console.log('服务器已启动 8000端口监听中...')
})

image-20240309195732430

使用原生JavaScript发送Ajax请求

Ajax如何发送get请求

需求:点击button,返回页面的结果到box里面,不刷新页面

// js服务端
const express = require('express');
const app = express();

app.get('/server', (request, response) => {
    // 设置响应头 这句话的作用是,设置允许跨域
    response.setHeader('Access-Control-Allow-Origin', '*');

    // 响应体
    response.send('Hello Ajax');
});

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>测试原生Ajax GET 请求</title>
    <style>
        #result {
            width: 200px;
            height: 100px;
            border: 1px solid #90b;
        }
    </style>
</head>
<body>
    <button>点击发送请求</button>
    <div id="result"></div>
    <script>
        // 获取button元素
        const btn = document.getElementsByTagName('button')[0];
        const result = document.getElementById('result');
        // 绑定事件
        btn.onclick = function(){
            // console.log('test');
            // 1. 创建对象
            // xhr这个只是变量名,名称可以随意,一般使用xhr因为控制台页有一个XHR,要看到XHR就能想到是Ajax
            const xhr = new XMLHttpRequest();
            // 2. 初始化,设置请求和方法
            // 请求方法指的是常见的HTTP请求,后面的就是给谁发,表示一个具体的url地址
            // 目前的阶段 http://127.0.0.1:8000  这个不建议省略,后面会补充
            // true参数表示使用异步请求,false表示同步请求
            // 当设置为true时,JavaScript会继续执行而不会等待服务器响应
            // 而设置为false时,JavaScript会等待服务器响应后再继续执行后续代码。通常情况下,我们会将该参数设置为true以确保不会阻塞页面加载。
            xhr.open('GET', 'http://127.0.0.1:8000/server', true); // 这里一般设置成true
            // 3. 发送
            xhr.send();
            // 4. 事件绑定 即处理服务端返回的结果
            // onreadystatechange 简单解析:
            // on 本身有when的意思 就是当...的时候
            // readstate 表示状态 有5个值分别是0 1 2 3 4 
            //               0 表示未初始化,最开始的属性就是0
            //               1 表示open方法已经调用完毕
            //               2 表示send方法已经调用完毕
            //               3 表示服务端返回了一部分的结果
            //               4 表示服务端返回了所有的结果
            // change 改变的时候
            xhr.onreadystatechange = function(){
                // 处理结果(服务端返回了所有的结果)
                if(xhr.readyState === 4){
                    // 再次判断响应状态码 200 401 403 404 500
                    // 2xx 开头的都表示成功 所以只要状态码在200-300(不含)之间,都是成功的
                    if(xhr.status >= 200 && xhr.status < 300){
                        // 处理结果 行 头 空行 体
                        // status里面保存的就是响应行的内容
                        console.log(xhr.status); // 状态码
                        console.log(xhr.statusText); // 状态字符串
                        console.log(xhr.getAllResponseHeaders()); // 所有的响应头
                        console.log(xhr.response); // 响应体
                        // 设置result的文本,将结果显示
                        result.innerHTML = xhr.response;
                    }
                };
            };
        };
    </script>
</body>
</html>

image-20240309205706408

Ajax如何发送get请求带参数

直接在url后面加上一个问号,然后带上参数就可以了,a=b的形式,然后如果有多个参数,使用&符号隔开即可

xhr.open('GET', 'http://127.0.0.1:8000/server?name=小满&age=3');

image-20240309210418561

Ajax如何发送post请求(不带参数)

需求:鼠标放到盒子上面,发送post请求,盒子上面显示服务器返回的结果

// 服务端
const express = require('express');
const app = express();

app.get('/server', (request, response) => {
    // 设置响应头 这句话的作用是,设置允许跨域
    response.setHeader('Access-Control-Allow-Origin', '*');

    // 响应体
    response.send('Hello Ajax from get');
});

app.post('/server', (request, response) => {
    // 设置响应头 这句话的作用是,设置允许跨域
    response.setHeader('Access-Control-Allow-Origin', '*');

    // 响应体
    response.send('Hello Ajax from post');
});

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>
    <style>
        #result {
            width: 200px;
            height: 100px;
            border: 1px solid #903;
        }
    </style>
</head>
<body>
    <div id="result"></div>
    <script>
        // 获取元素对象
        const result = document.getElementById('result');
        // 绑定事件
        result.addEventListener('mouseover', function(){
            // console.log('test');
            // 1. 创建对象
            const xhr = new XMLHttpRequest();
            // 2. 初始化 设置请求方式与url
            xhr.open('POST', 'http://127.0.0.1:8000/server');
            // 3. 发送
            xhr.send();
            // 4. 事件绑定
            xhr.onreadystatechange = function(){
                // 判断
                if(xhr.readyState === 4){
                    if(xhr.status >= 200 && xhr.status < 300){
                        // 处理服务端返回的结果
                        result.innerHTML = xhr.response;
                    };
                };
            };
        });
    </script>
</body>
</html>

image-20240309213152234

Ajax如何发送post请求(带参数)

post请求,参数是在send中去设置的

// 下面几种请求方式都可以,只要服务器有对应的处理方式即可,不过推荐使用  &  去连接
xhr.send('name=小满&age=3');

xhr.send('name:小满:age=3');

xhr.send('小满最棒了');

image-20240309213550049

Ajax如何设置请求头信息

// 设置请求头 接收两个参数,第一个是头的名字,第二个是头的值
// Content-Type 是用来设置请求体内容的类型的, 第二个参数是参数查询字符串的类型,这个是固定的写法
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');

image-20240309214709885

Ajax如何设置自定义的请求头

// 1.定义下面这个这个不需要前端去做,一般后端的程序员会做好,需要在服务端加上这句话,表示允许所有的请求头
response.setHeader('Access-Control-Allow-Headers', '*')

// 2. 将app.post改成all
// all 就是接收所有HTTP的请求 不论是get post delete ...
app.all('/server', (request, response) => {
    // 设置响应头 这句话的作用是,设置允许跨域
    response.setHeader('Access-Control-Allow-Origin', '*');
    // 响应头 允许所有的请求头
    response.setHeader('Access-Control-Allow-Headers', '*')
    // 响应体
    response.send('Hello Ajax from post');
});

image-20240309220029779

Ajax服务端如何响应json数据

需求:当打开客户端网页的时候,按下键盘上面任意按键就发送请求,然后等服务端返回结果,然后在div呈现结果

总结:

  1. 服务端的response.send,只能是字符串,如果要发json格式,需要response.send(JSON.stringify(data))
  2. 客户端转换json数据有两种方式
    1. 设置自动转换,一般在初始化之前,设置:xhr.responseType = 'json';,这样就可以自动把服务端的字符串转成json对象了。
    2. 手动转换,即调用JSON.parse(data);,然后操作即可。
<!-- 客户端 -->
    <!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        #result {
            width: 200px;
            height: 100px;
            border: 1px solid #80a;
        }
    </style>
</head>
<body>
    <div id="result"></div>
    <script>
        const result = document.getElementById('result');
        // 绑定键盘按下事件
        window.onkeydown = function(){
            // console.log('test');
            // 1. 向服务端发请求
            const xhr = new XMLHttpRequest();
            // 【自动转换】设置json响应体的类型为json
            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){
                        let data = xhr.response;
                        // 【手动转换】
                        // content = JSON.parse(data);
                        // name = content.name;
                        console.log(data);
                        result.innerHTML = data.name;
                    }
                }
            };
        };
    </script>
</body>
</html>
// 服务端
const express = require('express');
const app = express();

app.all('/json-server', (request, response) => {
    // 设置响应头 这句话的作用是,设置允许跨域
    response.setHeader('Access-Control-Allow-Origin', '*');
    // 响应头 允许所有的请求头
    response.setHeader('Access-Control-Allow-Headers', '*')
    // 响应体
    // 注意 send 对象只能接收一个字符串
    // response.send('Hello Ajax from json');
    // 如果要发送json就要做一个转换
    let data = {name: '小满', age: 3};
    response.send(JSON.stringify(data));
});

app.listen(8000, () => {
    console.log('服务已启动 8000端口监听中...')
})

image-20240309232906059

Ajax nodemon自动重启工具安装

插件官网:https://www.npmjs.com/package/nodemon

需要node环境

优点:修改服务端的内容之后不用再手动启动服务了,会自动重启服务。

  1. 安装
    1. npm install -g nodemon
  2. 启动
    1. 不用再通过node xx.js了 直接通过nodemon xx.js即可

image-20240309235101197

Ajax中IE缓存问题解决

什么是IE缓存,就是浏览器会对Ajax的请求结果做一个缓存,这样就导致下一次发送的请求走的是本地的缓存,而不是服务器的最新数据,就拿不到最新的数据了,这样对于时效性比较强的场景,Ajax缓存会影响到结果。

// 解决方案,加一个时间戳即可 时间戳就是Date.now() 
// 加在哪里? get的时候可以直接拼在路径后面
// 因为每一次的时间戳都是不一样的,所以浏览器会认为是两次不同的请求,就会返回最新的数据了
// 原始请求
xhr.open("GET", "http://127.0.0.1:8000/ie";
// 加上时间戳的请求
xhr.open("GET", "http://127.0.0.1:8000/ie?t=" + Date.now());

image-20240310001346690

Ajax请求超时控制与网络异常处理

我们的项目在上线之后,在允许过程中,一定会遇到网络请求超时的情况,我们不能保证服务端能一直及时快速响应,那么这个时候我们可以给Ajax来一个设置,给用户一个友好的提醒,并且在网络异常的时候也来一个友好的提醒,这样用户体验也会更好一点。

需求:点击按钮发送请求,设置等待时间2秒钟,如果2秒还没有响应,那么就来一个友好的提示

// 服务端
const express = require('express');
const app = express();


// 设置延时
app.get('/delay', (request, response) => {
    // 设置响应头 这句话的作用是,设置允许跨域
    response.setHeader('Access-Control-Allow-Origin', '*');
    setTimeout(()=>{
        response.send('延时响应');
    }, 3000) // 这里的3000 代表3秒钟
    // 响应体
});

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>
    <style>
        #result {
            width: 200px;
            height: 100px;
            border: 1px solid tomato;
        }
    </style>
</head>
<body>
    <div id="result"></div>
    <button id="btn">点我发送请求</button>
    <script>
        const result = document.getElementById('result');
        const btn = document.getElementsByTagName('button')[0];
        btn.addEventListener('click', ()=>{
            const xhr = new XMLHttpRequest();
            xhr.open('GET', 'http://127.0.0.1:8000/delay');
            xhr.send();
            // 设置延时 2000 2秒的意思 1000就是1秒
            // 如果设置的时间到了,还没有得到数据,那么正常的情况下就会取消
            xhr.timeout = 2000;
            // 超时回调函数
            xhr.ontimeout = ()=>{
                alert('网络异常,请稍后重试。');
            };
            // 网络异常回调
            xhr.onerror = ()=>{
                alert('您的网络似乎出现了一些问题,请稍后重试~');
            };
            xhr.onreadystatechange = ()=>{
                if(xhr.readyState === 4){
                    if(xhr.status >= 200 && xhr.status < 300){
                        result.innerHTML = xhr.response;
                    };
                }
            };
        });
    </script>
</body>
</html>

image-20240310004025906

image-20240310004505825

Ajax的取消请求

就是发送了Ajax请求之后,在结果还没有回来之前,手动把请求取消

使用abort即可取消请求

需求:定义两个button标签,第一个button标签用来发送请求,第二个button用来取消请求

// 服务端
const express = require('express');
const app = express();

// 设置延时
app.get('/delay', (request, response) => {
    // 设置响应头 这句话的作用是,设置允许跨域
    response.setHeader('Access-Control-Allow-Origin', '*');
    setTimeout(()=>{
        response.send('延时响应');
    }, 3000)
    // 响应体
});

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>取消请求</title>
</head>
<body>
    <button>发送请求</button>
    <button>取消请求</button>
    <script>
        const btns = document.querySelectorAll('button');
        // 注意 这里不能使用const声明函数,因为在不同的作用域内部,取消函数也需要使用到这个x
        let x = null;
        btns[0].onclick = ()=>{
            x = new XMLHttpRequest();
            x.open('GET', 'http://127.0.0.1:8000/delay');
            x.send();

        };
        btns[1].onclick = ()=>{
            // 取消请求的函数
            x.abort();
        };
    </script>
</body>
</html>

img

Ajax请求重复问题

如果说用户重复点击,比如上面动图重复点击发送请求,那么服务器的压力就会很大,而且会重复收到很多重复的请求,这样就会发生一个问题,用户频繁发请求,服务器频繁返回数据。

解决方案:

如果用户重复点击,当点击下一个的时候,就把上一次的请求自动取消掉,这样永远就只是有一个请求了。

  1. 标识变量,初始值设置为false;
  2. 然后再准备发送的时候设置成true,再请求结束之后再设置成false,可以参考下面的代码。
// 服务端
const express = require('express');
const app = express();


// 设置延时
app.get('/delay', (request, response) => {
    // 设置响应头 这句话的作用是,设置允许跨域
    response.setHeader('Access-Control-Allow-Origin', '*');
    setTimeout(()=>{
        response.send('延时响应');
    }, 3000)
    // 响应体
});

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>取消请求</title>
</head>
<body>
    <button>发送请求</button>
    <script>
        const btns = document.querySelectorAll('button');
        let x = null;
        // 标识变量 变量名随意就行
        // 是否正在发送给Ajax请求,false表示没有发送请求,状态为true表示在发送请求
        let isSending = false; 
        btns[0].onclick = ()=>{
            // 判断标识变量
            if(isSending) x.abort(); // 如果正在发送,则取消该请求,发送一个新的请求
            x = new XMLHttpRequest();
            // 修改状态定义再这个位置,当初始化之后就开始设置标识变量了
            // 也就是标识变量建议设置在初始化之前
            isSending = true;
            x.open('GET', 'http://127.0.0.1:8000/delay');
            x.send();
            x.onreadystatechange = () =>{
                if(x.readyState === 4){
                    // 这里取消,只要获取到全部数据就取消
                    isSending = false;
                };
            };
        };
    </script>
</body>
</html>

img

使用jQuery发送Ajax请求

使用jQuery如何发送Ajax请求(最简单)

需求:点击对应按钮,发送对应请求

<!-- 客户端 -->
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <!-- jQuery and JavaScript Bundle with Popper -->
        <!-- CSS -->
        <link
            href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/4.6.2/css/bootstrap.min.css"
            rel="stylesheet"
            integrity="sha384-xOolHFLEh07PJGoPkLv1IbcEPTNtaed2xpHsD9ESMhqIYd0nLMwNLD69Npy4HI+N"
            crossorigin="anonymous"
        />
        <script crossorigin="anonymous" src="https://cdn.bootcdn.net/ajax/libs/jquery/3.7.1/jquery.js"></script>
        <title>Document</title>
    </head>
    <body>
        <div class="container">
            <h2 class="page-header">jQuery发送Ajax请求</h2>
            <button class="btn btn-primary">GET</button>
            <button class="btn btn-danger">POST</button>
            <button class="btn btn-info">通用型方法Ajax</button>
        </div>
        <script>
            let data = {name: '小满', age: 3};
            // .eq(0):使用了jQuery的.eq()方法来筛选匹配元素集合中特定索引位置的元素。索引从0开始
            $('button').eq(0).click(function(){
                // 第1个参数,给谁发
                // 第2个参数,发送的内容,一般是一个对象
                // 第3个参数,response【变量名而已,可以自定义】是一个回调函数,就是成功之后的响应
                //           回调中的参数是响应体,可以前端对它做一些操作
                // 第4个参数,表示响应体类型,可以设置成json  也是比较常用的类型
                let url = 'http://127.0.0.1:8000/jquery-server';
                $.get(url, data, function(response){
                    console.log(response);
                }, 'json');
            });
            // 按钮2
            $('button').eq(1).click(function(){
                // 如果要发送post请求,这里改成post就可以了
                $.post('http://127.0.0.1:8000/jquery-server', data, function(response){
                    console.log(response);
                });
            });
        </script>
    </body>
</html>
// 服务端
// 引入express框架
const express = require('express');
// 引入中间件
const bodyParser = require('body-parser');
// 创建app
const app = express();

// jQuery服务
app.all('/jquery-server', (request, response) => {
    // 设置响应头 这句话的作用是,设置允许跨域
    response.setHeader('Access-Control-Allow-Origin', '*');
    // 响应头 允许所有的请求头
    response.setHeader('Access-Control-Allow-Headers', '*');
    // 响应体
    const data = {blog: '小满三岁啦!'}
    response.send(JSON.stringify(data));
});

app.listen(8000, () => {
    console.log('服务已启动 8000端口监听中...')
})

img

使用jQuery通用方法发送Ajax请求

// 按钮3 通用方法发送Ajax请求
$('button').eq(2).click(function(){
    $.ajax({
        // url 给谁发
        url: 'http://127.0.0.1:8000/jquery-server',
        // 发送参数
        data: {name: '小满', age: 3},
        // 请求类型,就是在这里对全部HTTP方法做一个设置
        type: 'GET',
        // 响应体结果设置【可选】
        contentType: 'json',
        // 超时时间 1000=1秒
        timeout: 2000,
        // 自定义请求头 服务端一定要设置 response.setHeader('Access-Control-Allow-Headers', '*');
        headers: {name: 'eva', age: 4},
        // 成功的回调
        success: function(response){
            console.log(response);
        },
        // 失败的回调
        error: function(){
            console.log('出错啦')
        }
    });
});
// 服务端
// 引入express框架
const express = require('express');
// 创建app
const app = express();

// jQuery服务
app.all('/jquery-server', (request, response) => {
    // 设置响应头 这句话的作用是,设置允许跨域
    response.setHeader('Access-Control-Allow-Origin', '*');
    // 响应头 允许所有的请求头
    response.setHeader('Access-Control-Allow-Headers', '*');
    // 响应体
    const data = {blog: '小满三岁啦!'}
    response.send(JSON.stringify(data));
});

app.listen(8000, () => {
    console.log('服务已启动 8000端口监听中...')
})

img

使用axios发送Ajax请求

get/post方式

axios官网:https://axios-http.com/docs/intro

也可以直接去https://www.bootcdn.cn/axios/获取api

<script src="https://cdn.bootcdn.net/ajax/libs/axios/1.2.0/axios.js"></script>

点击不同按钮发送不同的请求

<!-- 客户端 -->
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Axios 发送Ajax请求</title>
        <!-- 设置crossOrigin="anonymous" 解决引入api导致的跨域问题 -->
        <script
            crossorigin="anonymous"
            src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"
        ></script>
        <!-- CSS -->
        <link
            href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/4.6.2/css/bootstrap.min.css"
            rel="stylesheet"
            integrity="sha384-xOolHFLEh07PJGoPkLv1IbcEPTNtaed2xpHsD9ESMhqIYd0nLMwNLD69Npy4HI+N"
            crossorigin="anonymous"
        />
    </head>
    <body>
        <div class="container">
            <h2 class="page-header">使用Axios发送Ajax请求</h2>
            <button class="btn btn-primary">GET</button>
            <button class="btn btn-danger">POST</button>
        </div>
        <script>
            const btns = document.querySelectorAll("button");
            // 配置base_url
            // base_url 就是对路径做一个简化
            // 写上了这个之后,下面的路径直接简写就可以了,会自动做一个拼接
            axios.defaults.baseURL = "http://127.0.0.1:8000";
            btns[0].onclick = function () {
                // 给谁发 get请求演示
                axios
                    .get("/axios-server", {
                        // 传递参数,需要外面用一个大括号括起来
                        params: { name: "eva", age: 3 },
                        // 请求头
                        headers: { name: "eva", hobby: "fish" },
                        // 获取结果 通过.then()
                    })
                    .then(function (data) {
                        // axios返回的是一个对象,可以很方便的通过对象去取值
                        console.log(data);
                        // .then(data=>{console.log(data);};)  这样写一样
                    });
            };

            // post请求演示
            // 如果是post请求,那么第一个参数是url,第二个参数是请求体,第三个参数是其它配置
            btns[1].onclick = () => {
                axios     //  url            请求体
                    .post("/axios-server",{data: { username: "阿珂", password: 112233 }}, {
                        params: { name: "大乔", age: 4 },
                        headers: { name: "amigo", vip: 4 },
                    })
                    .then((value) => {
                        console.log(value);
                    });
            };
        </script>
    </body>
</html>
// 服务端
// 引入express框架
const express = require('express');
// 创建app
const app = express();


// axios
app.all('/axios-server', (request, response) => {
    // 设置响应头 这句话的作用是,设置允许跨域
    response.setHeader('Access-Control-Allow-Origin', '*');
    // 允许所有的请求头
    response.setHeader('Access-Control-Allow-Headers', '*');

    // 响应体
    const data = {blog: '小满三岁啦!'}
    response.send(JSON.stringify(data));
});

app.listen(8000, () => {
    console.log('服务已启动 8000端口监听中...')
})

img

axios通用方式发送Ajax请求

// 服务端
// 引入express框架
const express = require('express');
// 创建app
const app = express();


// axios
app.all('/axios-server', (request, response) => {
    // 设置响应头 这句话的作用是,设置允许跨域
    response.setHeader('Access-Control-Allow-Origin', '*');
    // 允许所有的请求头
    response.setHeader('Access-Control-Allow-Headers', '*');

    // 响应体
    const data = {blog: '小满三岁啦!'}
    response.send(JSON.stringify(data));
});

app.listen(8000, () => {
    console.log('服务已启动 8000端口监听中...')
})
// axios通用发送请求方式,非常清晰,推荐
// 完全是按照自上而下的方式
btns[2].onclick = () => {
    axios({
        // 请求方法
        method: 'POST',
        // url
        url: '/axios-server',
        // url参数
        params:{
            vip: 10,
            hobby: 'fish'
        },
        // 头信息
        headers: {
            username: 'eva'
        },
        // 请求体
        data: {
            username: '小满',
            password: 3
        }
    }).then((value)=>{
        // 响应状态码
        console.log(value.status);
        // 响应状态字符串
        console.log(value.statusText);
        // 响应头信息
        console.log(value.headers);
        // 响应体
        console.log(value.data);
    });
};

img

使用fetch函数发送Ajax请求

fetch函数的MDN网址:https://developer.mozilla.org/zh-CN/docs/Web/API/fetch

// 服务端
// 引入express框架
const express = require('express');
// 创建app
const app = express();

// fetch请求
app.all('/fetch-server', (request, response) => {
    // 设置响应头 这句话的作用是,设置允许跨域
    response.setHeader('Access-Control-Allow-Origin', '*');
    // 允许所有的请求头
    response.setHeader('Access-Control-Allow-Headers', '*');

    // 响应体
    const data = {blog: '小满三岁啦!'}
    response.send(JSON.stringify(data));
});

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>使用fetch函数发送Ajax请求</title>
        <!-- 设置crossOrigin="anonymous" 解决引入api导致的跨域问题 -->
        <script
            crossorigin="anonymous"
            src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"
        ></script>
        <!-- CSS -->
        <link
            href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/4.6.2/css/bootstrap.min.css"
            rel="stylesheet"
            integrity="sha384-xOolHFLEh07PJGoPkLv1IbcEPTNtaed2xpHsD9ESMhqIYd0nLMwNLD69Npy4HI+N"
            crossorigin="anonymous"
        />
    </head>
    <body>
        <div class="container">
            <h2 class="page-header">使用fetch函数发送Ajax请求</h2>
            <button class="btn btn-primary">发送</button>
        </div>
        <script>
            const btn = document.getElementsByTagName("button")[0];
            btn.onclick = () => {
                fetch(
                    // 请求地址
                    "http://127.0.0.1:8000/fetch-server",
                    {
                        // 请求方法
                        method: "POST",
                        // 请求头
                        headers: { username: "eva" },
                        // 请求体 可以通过字符串去传递,也可以通过对象去传递
                        body: "username=小满&password=123",
                    }
                )
                    .then((response) => {
                        // 返回的数据是.text() 也可以通过.json来转成json拿到数据
                        // return response.json();
                        return response.text();
                    })
                    .then((response) => {
                        console.log(response);
                    });
            };
        </script>
    </body>
</html>

img

跨域问题

同源策略

同源策略最早由Netscape公司提出,是浏览器的一种安全策略。

同源:协议、域名、端口号必须完全相同。

违背同源策略就是跨域

  • 比如a.com向b.com,http向https,3000端口向8000端口 发送请求,都是跨域。

Ajax是遵循同源策略的,也就是如果你满足同源策略,是无法发送Ajax请求的

<!-- 客户端 -->
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <!-- CSS -->
        <link
            href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/4.6.2/css/bootstrap.min.css"
            rel="stylesheet"
            integrity="sha384-xOolHFLEh07PJGoPkLv1IbcEPTNtaed2xpHsD9ESMhqIYd0nLMwNLD69Npy4HI+N"
            crossorigin="anonymous"
        />
        <title>演示同源策略</title>
    </head>
    <body>
        <div class="container">
            <h1 class="page-header">小满三岁啦!</h1>
            <button class="btn btn-primary">获取用户数据</button>
            <script>
                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){
                                console.log(xhr.response);
                            }
                        }
                    };
                };
            </script>
        </div>
    </body>
</html>
// 服务端
const express = require('express');
const app = express();

app.get('/home', (request, response)=>{
    // 响应一个页面使用下面的方法
    response.sendFile(__dirname + '/index.html');
})


app.get('/data', (request, response)=>{
    response.send('用户数据')
})

app.listen(9000, ()=>{
    console.log('服务已经启动...')
})

img

如何解决跨域

JSONP

  1. JSONP(JSON with Padding),是一个非官方的跨域解决方案,纯粹拼接程序员的聪明才智开发出来,只支持get请求
  2. JSONP是怎么工作的?
    1. 在网页有一些标签天生具有跨域能力,比如img link iframe script
    2. JSONP就是利用script标签的跨域能力来发送请求的。
  3. JSONP的使用
    1. 动态的创建一个script标签
      1. let script = document.createElement("script");
    2. 设置script的src,设置回调函数
      1. srcipt.src="http://localhost:端口/路径?callback=abc";
      2. eg: script.src="http://localhost:3000/testAJAX?callback=abc";

JSONP实践

需求:在输入框输入一个用户名,当input框失去焦点的时候向服务端发送请求,不论输入什么内容,服务端都返回“用户名已存在”,(假设我们目前没有数据库比对)然后把输入框变成红色。

<!-- -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    请输入用户名:<input type="text"><span></span>
    <!-- 用户名不存在的提示信息 -->
    <!-- <p></p> -->
    <script>
        // 获取对象
        const input = document.querySelector('input');
        const p = document.querySelector('span');
        // 声明handle函数 这个data从何而来?
        function handle(data){
            input.style.border = '1px solid red';
            // 修改p标签的提示文本
            p.innerHTML = data.msg;
            p.style.color = 'red';
        }

        // 绑定事件
        // 这里是失去焦点事件
        input.onblur =  function(){
            // 获取用户输入值 通过value
            let username = this.value;
            // 向服务端发送请求 检测用户是否存在
            // 1. 创建script标签
            const script = document.createElement("script");
            // 2. 设置标签的src属性,就是给谁发 给哪一个url地址发
            // script.src = 'http://127.0.0.1:9000/check-username?callback=handle';  这里不用写回调函数也是可以的
            script.src = 'http://127.0.0.1:9000/check-username';
            // 3. 将这个script插入到文档中,非常重要! 如果不插入浏览器是不会发送请求的。
            // 下面这行代码的意思是把script标签插入到body的最后
            document.body.appendChild(script);
        };   
    </script>
</body>
</html>
// 服务端
const express = require('express');
const app = express();

app.get('/check-username', (request, response) => {
    // 检测用户名是否存在
    const data = {
        exists: 1,
        msg: '用户名已存在'
    };
    // 将数据转化为字符串
    let content = JSON.stringify(data);
    // 返回结果
    // end和send的区别,end会马上返回 不会再发送任何数据
    response.send(`handle(${content})`);
})

app.listen(9000, ()=>{
    console.log('服务已经启动...')
})

jQuery发送JONSP解决跨域问题

// 服务端
const express = require('express');
const app = express();

app.get('/jquery-jsonp-server', (request, response) => {
    // 检测用户名是否存在
    const data = {
        name: '小满三岁啦',
        hobby: ['逃课', '欺负同学', '好逸恶劳']
    };
    // 将数据转化为字符串
    let content = JSON.stringify(data);
    // 接收callback参数
    let cb = request.query.callback;
    // 返回结果
    response.send(`${cb}(${content})`);
})

app.listen(9000, ()=>{
    console.log('服务已经启动...')
})
<!-- 客户端 -->
<!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 crossorigin="anonymous" src="https://cdn.bootcdn.net/ajax/libs/jquery/3.7.1/jquery.js"></script>
    <style>
        #result {
            width: 300px;
            height: 100px;
            border: 1px solid tomato;
            padding: 10px;
            color: red;
        }
    </style>
</head>
<body>
    <button>点击发送jsinp请求</button>
    <div id="result"></div>
    <script>
        $('button').eq(0).click(function(){
            // 注意这里的callback一定要按照这个格式去写 xx?callback=? 然后跟上一个回调函数
            $.getJSON('http://127.0.0.1:9000/jquery-jsonp-server?callback=?', function(data){
                // 打印结果
                console.log(data);
                // 将结果对应的值插入到div里面
                $('#result').html(`
                    姓名:${data.name}<br>
                    爱好:${data.hobby}
                `)
            })
        })
    </script>
</body>
</html>

img

设置CORS响应体解决跨域问题

  1. CORS是什么?

    1. CORS(Cross-Origin Resource Sharing),跨域资源共享。CORS是官方的跨域解决方案,它的特点是不需要再客户端做任何特殊的操作,完全再服务器中进行处理,支持getpost请求。跨域资源共享标准新增了一组HTTP 首字段,允许服务器声明哪些源站通过浏览器有权限访问哪些资源。
  2. CORS怎么工作的?

    1. CORS是通过设置一个响应头来告诉浏览器,该请求允许跨域,浏览器收到该响应以后就会对响应放行。
  3. CORS的使用

    1. 主要是在服务端的设置:

    2.  router.get("/testAJAX", function(req, res){});
      

可以参考一下MDN的这篇关于跨域的文章:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CORS

需求:点击按钮发送Ajax请求,结果在div呈现

// 服务端
const express = require('express');
const app = express();

app.all('/cors-server', (request, response)=>{
    // 设置响应头 这里的* 指的是所有端口,如果要指定端口可以写出明确的 eg: http:127.0.0.1:6000
    // 一般都是设置成 * 
    
    // 一般要解决跨域都是设置下面3个
    response.setHeader('Access-Control-Allow-Origin', '*');
    response.setHeader('Access-Control-Allow-Haders', '*');
    response.setHeader('Access-Control-Allow-Method', '*');
    response.send('hello cors');
})

app.listen(9000, ()=>{
    console.log('服务已经启动...')
})
<!-- 客户端 -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>cors解决跨域</title>
    <style>
        #result {
            width: 300px;
            height: 100px;
            border: 1px solid tomato;
        }
    </style>
</head>
<body>
    <button>点我发送请求</button>
    <div id="result"></div>
    <script>
        const btn = document.querySelector('button');
        btn.onclick = function(){
            // 1. 创建对象
            const xhr = new XMLHttpRequest();
            // 2. 初始化设置
            xhr.open('GET', 'http://127.0.0.1:9000/cors-server');
            // 3. 发送
            xhr.send();
            // 4. 绑定事件,处理结果
            xhr.onreadystatechange = function(){
                if(xhr.readyState === 4){
                    if(xhr.status >= 200 && xhr.status < 300){
                        // 输出响应体
                        console.log(xhr.response);
                    }
                }
            }
        };
    </script>
</body>
</html>

img

基于Ajax的练习

获取数据并清空数据

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>点击按钮 自动更新或者清空内容</title>
</head>
<body>
    <button>获取json</button> <!-- 第一个按钮用于获取JSON -->
    <button>清空json</button> <!-- 第二个按钮用于清空JSON -->
    <div id="json-content"></div> <!-- 用于显示JSON内容的div -->
    <script>
        // 获取按钮和JSON内容的元素
        const btnGetJson = document.getElementsByTagName('button')[0];
        const btnClaerJson = document.getElementsByTagName('button')[1];
        const jsonContent = document.querySelector("#json-content");

        // 点击“获取json”按钮时执行的操作
        btnGetJson.onclick = ()=>{
            // 创建一个新的XMLHttpRequest对象
            const xhr = new XMLHttpRequest();
            // true参数表示使用异步请求。当设置为true时,JavaScript会继续执行而不会等待服务器响应
            // 而设置为false时,JavaScript会等待服务器响应后再继续执行后续代码。
            // 通常情况下,我们会将该参数设置为true以确保不会阻塞页面加载。
            xhr.open('GET', 'https://jsonplaceholder.typicode.com/posts', true);
            xhr.send();
            xhr.responseType = "json";
            
            // 监听XMLHttpRequest对象的状态变化
            xhr.onreadystatechange = ()=>{
                if(xhr.readyState === 4){
                    if(xhr.status >= 200 && xhr.status < 300){
                        // 获取返回的JSON数据并构建新的内容
                        let data = xhr.response;
                        let newContent = "";
                        for(index in data){
                            newContent += "<p>" + data[index].body + "</p>";
                        };
                        // 将新内容填充到页面中
                        jsonContent.innerHTML = newContent;
                    };
                };
            };
        };

        // 点击“清空json”按钮时执行的操作
        btnClaerJson.onclick = () => {
            // 清空JSON内容的div
            jsonContent.innerHTML = "";
        }
    </script>
</body>
</html>

img

django的Ajax请求

Ajax提交urlencoded格式数据

Ajax给后台发送数据的默认编码格式是urlencoded,比如username=abcde&password=123456的形式。Django后端拿到符合urlencoded编码格式的数据都会自动帮你解析分装到request.POST中,与form表单提交的数据相同。

前端同上面一样,视图层可以参考下面

# 视图层
def home(request):
    if request.method == 'POST':
        data = request.POST
        num1 = int(data.get('num1'))
        num2 = int(data.get('num2'))
        return HttpResponse(f'{num1+num2}')
    return render(request, 'book/home.html')
<!-- 前端页面 -->
{% load static %}
<!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="{% static 'book/js/jquery-3.7.1.js' %}"></script>
        <link rel="stylesheet" href="{% static 'book/css/bootstrap.min.css' %}">
    </head>
    <body>
        <label for="num-one">输入第一个数字:</label>
        <input type="text" name="num-one" id="num-one" /><br />
        <label for="num-two">输入第二个数字:</label>
        <input type="text" name="num-two" id="num-two" /><br />
        <label>结果展示:<input type="text" id="result" /></label><br />
        <button class="btn btn-primary">点我计算结果</button>
        <script>
            // 绑定事件
            $(document).ready(function() {
                $('button').click(() => {
                    // 获取对象
                    const num1 = $('#num-one').val();
                    const num2 = $('#num-two').val();
                    $.ajax({
                        // url 不写就是当前地址
                        url: 'http://127.0.0.1:8000/home/',
                        // 参数
                        data: {num1: num1, num2: num2},
                        // 请求类型
                        type: 'POST',
                        // 指定响应类型
                        {#contentType: 'json',#}
                        // 超时时间  1000=1秒
                        {#ontimeout: 2000,#}
                        // 成功回调函数
                        success: function (response) {
                            console.log(response);
                            $("#result").val(response);
                        },
                        // 错误回调函数
                        error: function () {
                            alert('发生了一些错误');
                        },
                    });
                });
            });
        </script>
    </body>
</html>

Ajax怎么传递json数据

// 在前端使用 AJAX 发送 JSON 数据可以通过设置请求的 Content-Type 和将数据转换为 JSON 字符串来实现。

// 举例:使用 jQuery 发送包含 JSON 数据的 AJAX 请求。

// HTML 文件中引入 jQuery 库
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>

// JavaScript 代码
$(document).ready(function() {
    var jsonData = {
        key1: 'value1',
        key2: 'value2'
    };

    $.ajax({
        url: '/your-endpoint/',
        type: 'POST',
        // 主要是这里  加上这个就是json了 也可以直接写json
        // 这个表名要发向服务端的数据类型
        contentType: 'application/json',
        data: JSON.stringify(jsonData),
        // 表名接收的数据类型
        dataType: 'json',
        success: function(response) {
            console.log('Success:', response);
            // 在这里处理服务器返回的响应数据
        },
        error: function(xhr, status, error) {
            console.error('Error:', error);
            // 在这里处理请求错误
        }
    });
});
def demo(request):
    import json
    if request.method == "POST":
        if request.is_ajax():
            data = request.body.decode()
            print(">>>>>>>>>>>>>>>", type(json.loads(data)))
            print(data)
            return HttpResponse(f'{data}')
        # >>>>>>>>>>>>>>> <class 'dict'>
		# {"name":"小满","age":3}
    return render(request, 'mybook/demo.html')

使用 jQuery 发送文件数据

前端发送

<!-- 1. 设置表单的enctype属性 enctype="multipart/form-data" -->
<form enctype="multipart/form-data">
    {% csrf_token %}
    <input type="file" id="fileInput"><br>
    <!-- 2. 按钮必须是一个普通按钮,即button或者type属性为button -->
    <input type="button" value="文件上传" id="submit">
</form>
<script>
    $('#submit').click(function(){
        // 3. 用变量接收一个FormData() 对象 
        let formData = new FormData();
        // 4. append的第一个参数为后端使用request.FILES.get()可以取到的值
        //            第二个参数就是具体的文件对象,需要取两层
        formData.append("file", $("#fileInput")[0].files[0]);
        $.ajax({
			// 提交的地址,空字符串 '' 默认当前地址
            // 也可以写{% url 'upload' %}
            url:"/upload/",
            // 这里的formData就是上面定义的变量 已经有数据在里面了
            data:formData,
            // 指定提交方式
            type: "post",
            // processData: 默认为true 当设置为true的时候 jquery ajax 提交的时候不会序列化data,而是直接使用data
            processData: false,
            // 不使用默认的applicatio/x-www-from-rulencolded这中contentType
            // 分节符:目的是放置上传文件中出现分分解符号导致服务器无法正确识别文件起始位置
            // Ajax中contentType设置为false是为了避免Jquery对其操作,从而失去分界符
            contentType: false,
            success: function(response){
                console.log(response);
            },
            error: () => {
                alert("发生了一些错误");
            }
        })
    });
</script>

后端接收

from django.shortcuts import render, HttpResponse
from pathlib import Path


def test(request):
    if request.method == "POST" and request.is_ajax():
        # 获取文件数据 这个file 就是formData.append()的第一个参数设置的名称
        data = request.FILES.get('file')
        # 获取当前静态文件夹的路径
        dir_name = Path(__file__).parent / 'static'
        with open(rf"{dir_name}/{data}", 'wb') as f:
            # 写入文件, 一定不要忘记使用obj.chunks() 方法
            for line in data.chunks():
                f.write(line)
        return HttpResponse('OK')
    return render(request, 'test.html', locals())

img

Ajax实时渲染

// 实时渲染图片 通过Ajax
$(document).ready(function (){
    $("#adv_img").change(function(){
        // 创建一个阅读器对象
        let fileReader = new FileReader();
        // 获取当前input框的数据
        let fileData = $(this)[0].files[0];
        // 通过阅读器对象读取数据
        fileReader.readAsDataURL(fileData);
        // 渲染数据
        // 等待数据加载完毕
        fileReader.onload = function () {
            // 替换属性   fileReader.result 就是结果
            $("#img_adv_show").attr('src', fileReader.result);
        }
    })
})

img

通过jQuery遍历数组取值

// 点击按钮 发送Ajax请求
$("#add_adv_btn").click(function(){
    // 构造数据
    let formData = new FormData();
    $.each($("#form_adv_data").serializeArray(), (index, dataDict)=>{
        // 记得这里是value !!!!!  不是result
        formData.append(dataDict.name, dataDict.value);
    });
    $.ajax({
        url: '',
        type: 'POST',
        data: formData,
        contentType: false,
        contentData: false,
        success: function(response){
            alert(response);
        }
    });
}) 

更简单的写法

let formData = new FormData($('#form_adv_data')[0]);
// 等同于下面的写法
$.each($("#form_adv_data").serializeArray(), (index, dataDict) => {
   // 记得这里是value !!!!!  不是result
    formData.append(dataDict.name, dataDict.value);
});
posted @ 2024-03-23 00:49  小满三岁啦  阅读(15)  评论(0编辑  收藏  举报