【ajax】概述、发送请求 ( GET / POST )、解决跨域问题 ( JSONP / CORS )

一、概述

1、AJAX 简介

        AJAX 全称为 Asynchronous JavaScript And XML,就是异步的 JS 和 XML

        通过 AJAX 可以在浏览器中向服务器发送异步请求,最大的优势:无刷新获取数据

        AJAX 不是新的编程语言,而是一种将现有的标准组合在一起使用的新方式。

2、XML 和 JOSN 格式数据

2.1 XML

概念:XML是可扩展标记语言,被设计用来传输和存储数据。

XML 与 HTML:HTML 中都是预定义标签,而 XML 中没有预定义标签, 全都是自定义标签,用来表示一些数据。

 
    孙悟空 
    18 
     

2.2 JOSN

{"name":"孙悟空","age":18,"gender":"男"}

3、AJAX 的特点

3.1 AJAX 的优点

        (1)可以无需刷新页面而与服务器端进行通信。

        (2)允许你根据用户事件来更新部分页面内容。

3.2 AJAX 的缺点

        (1)没有浏览历史,不能回退.

        (2)存在跨域问题 ( 同源 ) 。

        (3)SEO 不友好(爬虫)。

4、AJAX 的使用

4.1 核心对象

        XMLHttpRequest,AJAX 的所有操作都是通过该对象进行的。

4.2 使用步骤

使用步骤:

        (1)创建 XMLHttpRequest 对象。

        (2)设置请求信息(可以设置请求头,看具体需要)。

        (3)发送请求。

        (4)接收响应。

示例:

    

二、AJAX 案例( GET / POST )

1、搭建测试 ajax 请求服务器(server.js)

// 1. 导入 express 模块
const express = require('express');

// 2. 创建 web 服务器
const app = express();

// 暴露静态资源
app.use(express.static(__dirname + '/src'))

// 3. 调用 app.listen(端口号,启动成功后的回调函数) 启动服务器
app.listen(8080, (err) => {
    if (!err) {
        console.log(`http://127.0.0.1:8080/01_4种状态.html`);
    }
})

2、使用 ajax 创建简单的 html 页面(xxx.html)

    

xhr 的 4 种状态:

        0:实例出来的那一刻状态就是0,初始状态。

        1:open 已经调用了,但是send 还没有调用,此时可以修改请求头内容。

        2:send 已经调用了,已经无法修改请求头。

        3:已经回来一部分数据,小数据会在此阶段一次性接收完毕,较大的数据有待进一步接收,响应头回来了。

        4:数据全部接收完毕。

3、GET / POST

3.1 GET

(1)在 server.js 中增加 GET 监听。

监听 GET 请求:
        (1)参数1:客户端请求的 URL 地址。
        (2)参数2:请求对应的处理函数
                              ① req:请求对象(包含了于请求相关的属性与方法)。
                              ② res:响应对象(包含了与响应相关的属性和方法)。

        如果 res.send() 中发送的是 json 格式的数据,可以在 xxx.html ==> xhr.open() 后面增加【xhr.responseType = 'json'】 来自动解析 json 格式数据。

app.get('/test_get', (req, res) => {
    console.log('hello, get!');
    // 接收 query 参数
    console.log(req.query);
    res.send(`--- hello, get! ---`)
})

(2)在 xxx.html 中设置 GET 请求。

GET 没有请求体,可以携带 query 和 params 参数。

query:http://127.0.0.1:8080/test_get?name=zs&age=20

params:http://127.0.0.1:8080/test_get/zs/20

// GET 请求
xhr.open('GET', 'http://127.0.0.1:8080/test_get?name=zs&age=20'); // query 参数
xhr.open('GET', 'http://127.0.0.1:8080/test_get/zs/20');  // params 参数

3.2 POST

(1)在 server.js 中增加 POST 监听。

监听 POST 请求:
        (1)参数1:客户端请求的 URL 地址。
        (2)参数2:请求对应的处理函数
                              ① req:请求对象(包含了于请求相关的属性与方法)。
                              ② res:响应对象(包含了与响应相关的属性和方法)。

app.post('/test_post', (req, res) => {
    console.log('hello, post!');
    // 接收 body 参数
    console.log(req.body);
    res.send(`--- hello, post! ---`)
})

(2)在 xxx.html 中设置 POST 请求。

POST 有请求体,一共有三种参数:query、 params、body(一般用body)。

body 有两种编码格式:urlencoded、json。

// POST 请求
xhr.open('POST', 'http://127.0.0.1:8080/test_post');  

 3.3 解析 POST 请求体中 urlencoded、json 格式的数据

(1)在 server.js 中使用中间件(中间件是 nodejs 里的东东)。

// 使用中间件解析 urlencoded 编码形式的请求体参数
app.use(express.urlencoded({ extended: true }));

// 使用中间件解析 json 编码形式的请求体参数
app.use(express.json());

(2)在 xxx.html 中追加请求头

// 追加响应头,用于标识携带请求体参数的编码形式 -- urlencoded
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded')

// 追加响应头,用于标识携带请求体参数的编码形式 -- json
xhr.setRequestHeader('Content-type', 'application/json')

3.4 完整 code

  server.js

// 1. 导入 express 模块
const express = require('express');

// 2. 创建 web 服务器
const app = express();

app.use(express.static(__dirname + '/src'))

app.use(express.urlencoded({ extended: true }));
app.use(express.json());

app.get('/test_get', (req, res) => {
    console.log('hello, get!');
    console.log(req.query);
    res.send(`--- hello, get! ---`)
})

app.post('/test_post', (req, res) => {
    console.log('hello, post!');
    console.log(req.body);
    res.send(`--- hello, post! ---`)
})

// 3. 调用 app.listen(端口号,启动成功后的回调函数) 启动服务器
app.listen(8080, (err) => {
    if (!err) {
        console.log(`http://127.0.0.1:8080/01_4种状态.html`);
    }
})

  xxx.html


    
    

三、缓存问题、请求异常与超时(了解)

1、解决 IE 浏览器 GET 缓存的问题

        问题:IE 浏览器存在强缓存机制,ajax 只会发送的第一次请求,剩余多次请求不会在发送给浏览器而是直接加载缓存中的数据。(简单的说,就是当我第二次发送请求时,浏览器发现地址没有变,并没有给服务器发送请求,直接显示第一次请求的信息,这就导致在相同地址下,我发送了不同的数据但是没有进行更改。)

        解决方案:浏览器的缓存是根据 url 地址来记录的,所以我们只需要修改 url 地址即可避免缓存问题。

// 增加时间戳保证 URL 地址不同
xhr.open("get","/testAJAX?t="+Date.now());

2、请求异常与超时的处理

// 配置出错的回调
xhr.onerror = () => {
    console.log('当前网络不稳定,请稍后重试');
}

// 设定超时时间 ( 2000 就是 2ms )
xhr.timeout = 2000

// 设置超时的回调
xhr.ontimeout = () => {
    console.log('网速不给力,请稍后重试');
}

四、取消请求、避免多次重复请求

1、取消请求

// 语法
xhr.abort()

        思考:取消请求之前这个请求有没有到达服务器?

        答:存在两种可能:① 还没有发送到服务器就被取消了。② 已经发送到服务器并且返回了信息,但是不要了。 

2、避免多次重复请求

        基本思想:在创建下一个 xhr 实例之前先判断一下目前的 xhr 是不是正在加载中。如果是,则取消之前的请求,创建新的 xhr 实例。

		

五、跨域与同源问题(重要!)

1、概述

1.1 为什么会有跨域的问题?

        原因是浏览器为了安全,而采用的同源策略 ( Same origin policy )。

1.2 什么是同源策略?

        (1)同源策略是由 Netscape 提出的一个著名的安全策略,现在所有支持 JavaScript 的浏览器都会使用这个策略。

        (2)Web 是构建在同源策略基础智商的,浏览器只是针对同源策略的一种实现。

        (3)同源:协议、域名(IP)、端口必须要完全相同,即协议、域名(IP)、端口都相同,才算是在同一个域里。   

请求地址 形式 结果
http://study.cn/test/a.html 协议、域名、端口都相同 成功
http://a.study.cn/test/a.html 域名不同 失败
http://study.cn:8080/test/a.html 端口不同 失败
https://study.cn/test/a.html 协议不同 失败

1.3 非同源(跨域)受到哪些限制?

        (1)Cookie 不能读取。

        (2)DOM 无法获得。

        (3)AJAX 请求不能获取数据。【注意:是不能获取数据,并不是不能发送请求】

2、JSONP 解决跨域问题

2.1 JSONP 是什么?

        JSONP(JSON with Padding),是一个非官方的跨域解决方案,纯粹凭借程序员的聪明 才智开发出来,只支持 get 请求

2.2 JSONP 是怎么工作的?

        在网页有一些标签天生具有跨域能力,比如:img link iframe script。

        JSONP 就是利用 script 标签的跨域能力来发送请求的。

2.3 JSONP 的实现

// xxx.html

    
    



// server.js
app.get('/test_jsonp', (req, res) => {
    console.log('收到请求!');
    const { callback } = req.query
    const person = { name: 'zs', age: 20 }
    res.send(`${callback}(${JSON.stringify(person)})`)
})

注意点:

        模板字符串里的内容要是字符串,直接传 person 对象默认使用 toString() ,最终得到结果是 demo([Object Object]),这明显是错误的。要使得对象能够变成字符串并且还能获取到其中的内容,就需要用到JSON.stringify()。

res.send(`${callback}(${JSON.stringify(person)})`)

2.4 JSONP 面试题

(1)jsonp 是如何解决跨域问题的?

        答:jsonp 解决跨域问题的原理就是绕开 xhr借助 script 标签的跨域能力来发送请求。

(2)json 和 jsonp 有关系吗?

        答:有一定的关系,但是两者是两回事。json 是一种存储和交互数据的格式,jsonp 是后端解决跨域问题的一种方式。

        关系:jsonp 后端返回数据的时候必须得是字符串的形式。

(3)jsonp 解决跨域问题的时候也是用到了 xhr,对吗?

        答:不对,jsonp 绕开了 xhr。

(4)jsonp 解决跨域存在什么优缺点?

        答:JSONP的优缺点_SalmonellaVaccine的博客-CSDN博客

3、CORS 解决跨域问题

3.1 CORS 是什么?

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

3.2 CORS 是怎么工作的?

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

3.3 利用响应头实现 CORS 

3.3.1 简单请求 ( GET / POST )

app.get('/test_cors_get', (req, res) => {
    // res.setHeader('Access-Control-Allow-Origin', 'http://127.0.0.1:5500');
    res.setHeader('Access-Control-Allow-Origin', '*');
    // Access-Control-Expose-Headers 列出了哪些首部可以作为响应的一部分暴露给外部
    res.setHeader('Access-Control-Expose-Headers', '*');
    res.send('hello_cors_get!');
})

3.3.2 复杂请求 ( PUT / DELETE / ... )

复杂请求向服务器发送了两次请求:预检请求、实际请求。

若不处理预检请求,则第一次请求失败,依旧会报跨域错误:

处理预检请求后:

预检请求:

        在 CORS 中,可以使用 OPTIONS 方法发起一个预检请求,以检测实际请求是否可以被服务器所接受。预检请求报文中的 Access-Control-Request-Method 首部字段告知服务器实际请求所使用的 HTTP 方法;Access-Control-Request-Headers 首部字段告知服务器实际请求所携带的自定义首部字段。服务器基于从预检请求获得的信息来判断,是否接受接下来的实际请求。

        参考:OPTIONS - HTTP | MDN

app.options('/test_cors_put', (req, res) => {
    res.setHeader('Access-Control-Allow-Origin', '*');
    res.setHeader('Access-Control-Expose-Headers', '*');
    // Access-Control-Allow-Methods 在对 preflight request.(预检请求)的应答中
    // 明确了客户端所要访问的资源允许使用的方法或方法列表
    res.setHeader('Access-Control-Allow-Methods', '*');
    res.send();
})

app.put('/test_cors_put', (req, res) => {
    res.setHeader('Access-Control-Allow-Origin', '*');
    res.setHeader('Access-Control-Expose-Headers', '*');
    res.send('hello_cors_put!');
})

3.4 利用中间件实现 CORS 

(1)先下载 cors,我用的 npm。

npm install cors

(2)按照之前的基本结构正常的发送请求信息即可。

// server.js

// 1. 导入 express 模块
const express = require('express');
// 1. 导入 cors 模块
const cors = require('cors');

// 2. 创建 web 服务器
const app = express();

app.use(express.static(__dirname + '/src'))

// 3. 全局生效的中间件
app.use(cors())

// 4. 匹配需要的请求
app.get('/test_cors_get', (req, res) => {
    res.send('hello_cors_get!');
})

app.put('/test_cors_put', (req, res) => {
    res.send('hello_cors_put!');
})

你可能感兴趣的:(前端,ajax,servlet,javascript,node.js)