Ajax前后端交互利器详解

Posted 生命是有光的

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Ajax前后端交互利器详解相关的知识,希望对你有一定的参考价值。

✍Ajax前后端交互利器

🔥Ajax前后端交互利器地址
🔥Ajax前后端交互利器详解(一)https://blog.csdn.net/Augenstern_QXL/article/details/120116296
🔥Ajax前后端交互利器详解(二)https://blog.csdn.net/Augenstern_QXL/article/details/120116401

1、同源政策

1.1、Ajax请求限制

Ajax 只能向自己的服务器发送请求。比如现在有一个A网站、有一个B网站,A网站中的 html 文件只能向A网站服务器中发送 Ajax 请求,B网站中的 HTML 文件只能向 B 网站中发送 Ajax 请求,但是 A 网站是不能向 B 网站发送 Ajax请求的,同理,B 网站也不能向 A 网站发送 Ajax请求。

1.2、什么是同源

如果两个页面拥有相同的协议、域名和端口,那么这两个页面就属于同一个源,其中只要有一个不相同,就是不同源

例如我们开启两个服务器,一个端口是3000,另一个端口是3001

// 引入express框架
const express = require('express');
// 路径处理模块
const path = require('path');
// 创建web服务器
const app = express();
// 静态资源访问服务功能
app.use(express.static(path.join(__dirname, 'public')));
// 监听端口
app.listen(3000);
// 引入express框架
const express = require('express');
// 路径处理模块
const path = require('path');
// 创建web服务器
const app = express();
// 静态资源访问服务功能
app.use(express.static(path.join(__dirname, 'public')));
// 创建路由
app.get('/test', (req, res) => {
    res.send("ok");
});
// 监听端口
app.listen(3001);

我们在端口为3000的客户端发送ajax请求至端口为3001的服务器

<body>
	<script type="text/javascript" src="/js/ajax.js"></script>
	<script type="text/javascript">
		ajax({
			url: 'http://localhost:3001/test',
			type: 'get',
			success: function (result) {
				console.log(result);
			}
		})
	</script>
</body>

1.3、同源政策的目的

  • 同源政策是为了保证用户信息的安全,防止恶意的网站窃取数据。最初的同源政策是指 A 网站在客户端设置的 Cookie,B网站是不能访问的。

  • 随着互联网的发展,同源政策也越来越严格,在不同源的情况下,其中有一项规定就是无法向非同源地址发送Ajax 请求,如果请求,浏览器就会报错

1.4、使用JSONP解决同源限制问题

jsonp 是 json with padding 的缩写,它不属于 Ajax 请求,但它可以模拟 Ajax 请求

  1. 将不同源的服务器端请求地址写在 script 标签的 src 属性中
<script src="www.example.com"></script>
<script src=“https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
  1. 服务器端响应数据必须是一个函数的调用,真正要发送给客户端的数据需要作为函数调用的参数。
const data = 'fn({name: "张三", age: "20"})';
res.send(data);
  1. 客户端全局作用域下定义函数 fn

    注意要将函数定义放在 script 标签的前面,因为 script 标签加载完服务器端的响应内容以后会直接调用这个准备好的函数,如果客户端没有定义这个函数,函数在调用时找不到这个函数的定义部分,代码将会报错

function fn (data) {
    
}
  1. 在 fn 函数内部对服务器端返回的数据进行处理
function fn (data) { 
    console.log(data); 
}

1.4.1、示例

我们开启两个服务器,一个端口是3000,另一个端口是3001,我们在端口为3000的客户端发送ajax请求至端口为3001的服务器

<body>
	<script>
		function fn (data) {
            // 在客户端定义函数
			console.log('客户端的fn函数被调用了')
			console.log(data);
		}
	</script>
	<!-- 1.将非同源服务器端的请求地址写在script标签的src属性中 -->
	<script src="http://localhost:3001/test"></script>
</body>

在服务器端调用函数

// 引入express框架
const express = require('express');
// 路径处理模块
const path = require('path');
// 创建web服务器
const app = express();
// 静态资源访问服务功能
app.use(express.static(path.join(__dirname, 'public')));
// 创建路由
app.get('/test', (req, res) => {
    // 在服务器调用函数
    const result = 'fn({name: "张三"})';
    res.send(result);
});
// 监听端口
app.listen(3001);

1.5、JSONP代码优化

  1. 客户端需要将函数名称传递到服务器端
  2. 将 script 请求的发送变成动态请求。
<body>
	<script>
		function fn (data) {
			console.log('客户端的fn函数被调用了')
			console.log(data);
		}
	</script>
	<!-- 1.将非同源服务器端的请求地址写在script标签的src属性中 -->
	<script src="http://localhost:3001/better?callback=fn"></script>
</body>

在非同源服务器端进行接收客户端传递的函数名称

// 引入express框架
const express = require('express');
// 路径处理模块
const path = require('path');
// 创建web服务器
const app = express();
// 静态资源访问服务功能
app.use(express.static(path.join(__dirname, 'public')));
// 创建路由
app.get('/better', (req, res) => {
    // 接收客户端传递过来的函数的名称
    const fnName = req.query.callback;
    // 将函数名称对应的函数调用代码返回给客户端
    const result = fnName + '({"name": "张三"})';
    res.send(result);
});
// 监听端口
app.listen(3001);

我们将函数名称进行改变为f1,也使得 callback 的值改变为f1

  1. 封装 jsonp 函数,方便请求发送
function jsonp (options) {
	// 动态创建script标签
	var script = document.createElement('script');
	// 拼接字符串的变量
	var params = '';

	for (var attr in options.data) {
		params += '&' + attr + '=' + options.data[attr];
	}
	
	// myJsonp0124741
	var fnName = 'myJsonp' + Math.random().toString().replace('.', '');
	// 它已经不是一个全局函数了
	// 我们要想办法将它变成全局函数
	window[fnName] = options.success;
	// 为script标签添加src属性
	script.src = options.url + '?callback=' + fnName + params;
	// 将script标签追加到页面中
	document.body.appendChild(script);
	// 为script标签添加onload事件
	script.onload = function () {
		document.body.removeChild(script);
	}
}

我们也可以说是封装了 JSONP 函数,我们可以将 JSONP 抽离为 jsonp.js文件,这样我们在客户端就可以引入 jsonp.js 文件使用了

  1. 服务器端代码优化之 res.jsonp 方法。

    服务器端接收客户端传递过来的函数名称,并且拼接函数调用,在函数调用的内部,我们还需要将真实的数据写在里面,如果数据是从数据库查出来的 json 对象,我们还需要先转换成 json 字符串,比较麻烦,express 框架给我们 res 下提供了 res.jsonp 方法

app.get('/better',(req,res)=>{
    res.jsonp({name:"lisi",age:20})
})

1.6、CORS跨域资源共享

CORS:全称为 Cross-origin resource sharing,即跨域资源共享,它允许浏览器向跨域服务器发送 Ajax 请求,克服了 Ajax 只能同源使用的限制。

1.7、访问非同源数据服务器端

同源政策是浏览器给予 Ajax 技术的限制,服务器端是不存在同源政策限制,服务器端可以直接访问非同源网站中的数据。

所以对于客户端来讲,如果要获取非同源网站中的数据,可以让自己的服务器端获取非同源网站中的数据,等到自己的服务器端获取到数据之后,自己网站的服务器端再将数据响应到客户端,这样就绕过了浏览器的同源政策限制。

我们开启两个服务器,一个端口是3000,另一个端口是3001

客户端代码如下:

  • 客户端访问自己的服务器
<body>
	<button id="btn">点我发送请求</button>
	<script src="/js/ajax.js"></script>
	<script>
		// 获取按钮
		var btn = document.getElementById('btn');
		// 为按钮添加点击事件
		btn.onclick = function () {
			ajax({
				type: 'get',
				url: 'http://localhost:3000/server',
				success: function (data) {
					console.log(data);
				}
			})
		};
	</script>
</body>

服务器3000端代码如下:

  • 服务器端获取非同源网站的数据,需要引入第三方模块 request
// 引入express框架
const express = require('express');
// 路径处理模块
const path = require('path');
// 向其他服务器端请求数据的模块
const request = require('request');
// 创建web服务器
const app = express();
// 静态资源访问服务功能
app.use(express.static(path.join(__dirname, 'public')));

app.get('/server', (req, res) => {
    request('http://localhost:3001/cross', (err, response, body) => {
        /**
         * 第一个参数是请求地址
         * 第二个参数是回调函数
         */
        res.send(body);
    })
});

// 监听端口
app.listen(3000);

服务器3001端的代码如下:

// 引入express框架
const express = require('express');
// 路径处理模块
const path = require('path');
// 创建web服务器
const app = express();
// 静态资源访问服务功能
app.use(express.static(path.join(__dirname, 'public')));
// 创建路由
app.get('/cross', (req, res) => {
    res.send('ok')
});
// 监听端口
app.listen(3001);

6.6 CORS跨域资源共享

origin: http://localhost:3000
Access-Control-Allow-Origin: 'http://localhost:3000'
 Access-Control-Allow-Origin: '*'

Node 服务器端设置响应头示例代码:

app.use((req, res, next) => {
     res.header('Access-Control-Allow-Origin', '*');
     res.header('Access-Control-Allow-Methods', 'GET, POST');
     next();
 })

6.9 withCredentials属性

使用Ajax技术发送跨域请求时,默认情况下不会在请求中携带cookie信息。但是如果两台服务器都是我们自己的,我们想要实现跨域请求,需要在客户端和服务器端进行处理

在客户端 ajax 对象下有一个属性:

  • withCredentials:指定在涉及到跨域请求时,是否携带cookie信息,默认值为false

在服务器端响应头中设置字段

  • Access-Control-Allow-Credentials:true 允 许客户端发送请求时携带cookie

2、jQuery中的Ajax

2.1、$.ajax()方法概述

$.ajax()方法作用:发送 Ajax 请求

2.2、发送Ajax请求

$.ajax({
     type: 'get',
     url: 'http://www.example.com',
     data: { name: 'zhangsan', age: '20' },
     contentType: 'application/x-www-form-urlencoded',
     beforeSend: function () { 
         return false
     },
     success: function (response) {},
     error: function (xhr) {}
});
  • type:代表请求方式

  • url:代表请求地址

  • data:代表向服务器端发送的请求参数,它可以是一个对象,在内部会将其转化为参数字符串,除了传递对象以外,我们也可以传递字符串参数值,在内部都会将其转化为参数字符串进行发送

    {
    	data: 'name=zhangsan&age=20'
    }
    
  • contentType:告诉服务器端客户端要向服务器端传递的参数格式类型,默认是application/x-www-form-urlencoded,也就是 参数名 = 参数值,多个参数之间用 & 分隔的参数字符串,如果传递的是 json 格式的请求参数,需要将 contentType 进行如下替换

    {
         contentType: 'application/json'
    }
    

    然后在 data 中传递 json 格式字符串,需要通过 JSON.stringify 将json对象转换为字符串

    JSON.stringfy({ name: 'zhangsan', age: '20' })
    
  • beforeSend:允许我们在请求发送之前做一些事,是一个函数,比如在请求发送之前我们可以对请求参数进行格式验证,格式不正确,return false 请求便不会发送了

  • success:是一个函数,请求发送成功之后就会被调用,有一个形参,这个形参就是服务器端返回的数据

  • error:是一个函数,请求失败之后就会被调用,接收一个 ajax 对象,我们可以在对象中获取错误信息,并且根据错误信息做出错误处理

例如,点击按钮发送 ajax 请求,客户端代码如下:

<body>
	<button id="btn">发送请求</button>
	<script src="/js/jquery.min.js"></script>
	<script>
		$('#btn').on('click', function () {
			$.ajax({
				// 请求方式
				type: 'get',
				// 请求地址
				url: 'http://localhost:3000/base',
				// 请求成功以后函数被调用
				success: function (response) {
					// response为服务器端返回的数据
					// 方法内部会自动将json字符串转换为json对象
					console.log(response);
				},
				// 请求失败以后函数被调用
				error: function (xhr) {
					console.log(xhr)
				}
			})
		});
	</script>
</body>

服务器端代码如下:

// 引入express框架
const express = require('express');
// 路径处理模块
const path = require('path');
// 创建web服务器
const app = express();
// 静态资源访问服务功能
app.use(express.static(path.join(__dirname, 'public')));
app.get('/base', (req, res) => {
    res.send({
        name: 'zhangsan',
        age: 30
    })
});

// 监听端口
app.listen(3000);

如果我们是 post 请求方式:

客户端代码如下:

<body>
    <button id="btn">发送请求</button>
    <script src="/js/jquery.min.js"></script>
    <script>
        $('#btn').on('click', function() {
            $.ajax({
                // 请求方式
                type: 'post',
                // 请求地址
                url: 'http://localhost:3000/base',
                // 请求成功以后函数被调用
                以上是关于Ajax前后端交互利器详解的主要内容,如果未能解决你的问题,请参考以下文章

前端前后端交互重点Ajaxの介绍及实战

AJAX-前后端交互的艺术

ajax前后端交互原理

前后端交互之封装Ajax+SpringMVC源码分析

SSM-网站后台管理系统制作---Ajax前后端交互

前后端交互