AJAX基础和跨域问题和JSON
这里是knockkey,这是断了昨天但依旧坚持更新的第五天
1. 同源策略
- 同源策略(Same-Origin-Policy) 最早是由 Netscape 公司提出,是浏览器的一种安全策略。即不同源的页面之间,不准互相访问数据
- window.origin 或 location.origin 可以得到当前源
- 协议 域名 端口 都相等, 违背同源策略就是跨域
- ajax默认是遵循同源策略的。
实现跨域的方式
2. CORS
- CORS是一个W3C标准,全称是
跨域资源共享(Cross-origin resource sharing)。 - 它允许浏览器向跨源服务器,发出
XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。 - 但是IE10以下是不支持的。
- CORS的整个过程都由浏览器自动完成,前端无需做任何设置,跟平时发送ajax请求并无差异。so,实现CORS的关键在于服务器,只要服务器实现CORS接口,就可以实现跨域通信。
请求类型
- 简单请求
- 请求方式使用下列方法之一
- GET
- POST
- HEAD
- Content-Type 的值仅限于下列三者之一:
- text/plain
- multipart/form-data
- application/x-www-form-urlencoded
- 请求方式使用下列方法之一
对于简单请求,浏览器会直接发送CORS请求,具体说来就是在header中加入origin请求头字段。同样,在响应头中,返回服务器设置的相关CORS头部字段,Access-Control-Allow-Origin字段为允许跨域请求的源。请求时浏览器在请求头的Origin中说明请求的源,服务器收到后发现允许该源跨域请求,则会成功返回
- 非简单请求
- 略
如何使用
因为CORS与AJAX没有什么差别,只不过浏览器发现是AJAX请求跨源,就会在请求中自动添加一些附加的头信息。因此我们只需要以此判断是否可以跨域,在响应头中加入一些信息即可。 所以,CORS跨域的关键是在服务器,只要服务器实现了CORS接口,即可跨源通信。 简单请求:
response.setHeader('Access-Control-Allow-Origin', 'http://wangpf.com:9990')
注意:它的值要么是请求时Origin字段的值(协议 + 域名 + 端口),要么是一个*,表示接受任意域名的请求。
AJAX 和服务器代码展示:
前端内容:
const req = new XMLHttpRequest()
req.open('GET', 'http://localhost:3001')
req.send()
req.onreadystatechange = () => {
if (req.readyState === 4) {
if (req.status >= 200 && req.status < 300) {
console.log(req.response);
}
}
}
服务端内容
var express = require('express');
var app = express();
var allowCrossDomain = function (req, res, next) {
res.header('Access-Control-Allow-Origin', 'http://localhost:3001');
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
res.header('Access-Control-Allow-Headers', 'Content-Type');
next();
}
app.use(allowCrossDomain);
- 接下来,
http://localhost:3001下的GET,PUT,POST,DELETE请求,自定义首部字段为Content-Type的非简单请求则会被正常访问,当然,你也可以将Access-control-Allow-Methods和Access-Control-Allow-Headers这两个配置删掉,删掉之后,将仅支持简单请求进行跨域。
CROS字段
Access-Control-Allow-Origin- 该字段必填,允许请求的域
Access-Control-Allow-Methods- 该字段必填,它的值是逗号分隔的一个字符串,表明服务器支持的所有跨域请求的方法。注意,返回的是所有支持的方法,而不单是浏览器请求的那个方法。这是为了避免多次"预检"请求。
Access-Control-Allow-Headers- 可选。如果浏览器请求包括Access-Control-Request-Headers字段,则Access-Control-Allow-Headers字段是必需的。它也是一个逗号分隔的字符串,表明服务器支持的所有头信息字段,不限于浏览器在"预检"中请求的字段。预检请求后,告知发送请求需要有的头部
JSON:前后端交换数据的一种格式
JSON字符串:往往都是在后端向前端发送数据时传输的格式。JSON对象:往往都是前端希望得到的。数据或对象或它们之间的嵌套。- JSON其实就是JavaScript中的一个
对象,跟var obj={}在质上完全一样,只是在量上可以无限扩展。简单地讲,json其实就是JavaScript中的对象(Object)和数组(Array,其实也是对象)这倆在那儿你嵌我我嵌你地套上n多层,以此模拟出许多复杂的数据结构。
JSON字符串与JSON对象之间的转换
- JSON.parse(JSON格式的字符串):正解析,将JSON字符串转换为JSON对象,没有副作用。
- JSON.stringify(JSON对象):反解析,将JSON对象转换为JSON字符串,没有副作用。
2. JSONP: 是一种跨域数据交互协议
- JSONP(JSONP with Padding) 是一大堆牛逼的程序员想出来的跨域方法。
- json是理想的数据交换格式,但没办法跨域直接获取,于是就将json包裹(padding)在一个合法的js语句中作为js文件传过去
- 当前网站创造一个script标签去请求另一个网站的JS,然后这个JS文件会夹带一些数据,这些数据会在我的网站上调用全局函数运行。
JSONP是怎么实现的?
- 利用
动态创建一个script标签并利用它的src属性向服务器发送一次HTTP请求,并提前声明好一个回调函数,回调函数的函数名利用callback请求参数传递给后端。后端接收到来自前端的请求后,获取callback请求的请求参数并拼接一个调用函数的JS代码段并将要返回给前端的数据以实参的形式存在。前端接收到来自后端的响应后,会将后端的返回内容当做JS代码来执行即调用一个函数,并用一个形参来接收后端向要传递过来的数据。
JSONP的优点和缺点
优点:
- 支持IE
缺点:
-
由于它是script标签,它不能像AJAX一样,读出(拿到)状态码和响应头,它只知道成功和失败(用onload和onerror来监听)。
-
由于它是script标签,它只能发get请求,不支持POST。
JSONP的原理
-
网页有一些标签天生具有跨域的能力,比如:img link iframe script
-
JSONP 就是利用
script标签的跨域能力来发送请求的(这也是jsonp跨域只能用get请求的原因所在) -
首先在客户端注册一个callback,然后把callback的名字传给服务器。此时,服务器先生成 json 数据,然后以 javascript 语法的方式,生成一个function , function 名字就是传递上来的参数 jsonp。最后将 json 数据直接以入参的方式,放置到 function 中,这样就生成了一段 js 语法的文档,返回给客户端。
-
jsonp原理是这样的,假设,网站A需要获取网站B的数据,网站B说我给你们一个方法,【1. 你们使用
<script src="http://www.B.com/open.js"></script>标签先获取到open.js文件(网站B的责任),这里边有你们需要的数据。2. 你们获取数据后处理数据(总得处理数据吧)的方法名必须命名为foo(数据请求者的责任和义务)】,这里相当于B网站和请求获取数据者之间建立了一个协议,要求请求者务必按照规则办事,如果请求者不能同时遵守上面两条就不能按预期获取数据。 -
网站A,网站B都非常高兴,那么问题又来了,网站C说也需要获取网站B的数据,网站B把协议甩给它,网站C拿过来一看,foo这个名字已经在自己的脚本文件的6868行用过了,而且已经使用在脚本的各个角落,批量替换会导致很多潜在bug啊,网站B情急之下决定把foo改成fool,网站A立马蹦起来,因为自己的网站已经在很多地方使用foo引用了数据。为了避免上面情况发生,那些牛X哄哄的开发者使用了动态生成js文件的方法.
//封装JSONP函数 使用JSONP 实现
function JSONP(url) {
return new Promise((resolve, reject) => {
const random = 'WangpfCallbackName' + Math.random()
window[random] = (data) => {
resolve(data)
}
const script = document.createElement('script')
script.src = `${url}?callback=${random}`
document.body.appendChild(script)
script.onload = () => {
script.remove()
}
script.onerror = () => {
reject()
}
})
}
JSONP('http://xxxx.com:8888/friends.js')
.then((data) => {
console.log(data);
})
- 无封装
// 回调函数:当成功接收到来自后端的响应之后会自动调用这个函数,并且该函数中的形参就代表了后端要返回的数据
function jsonpcallback(data) {
console.log(data[0])
}
query.onclick = function () {
let script = document.createElement('script');
script.setAttribute('src', "http://xx.xx.xxx.xxx/data?callback=jsonpcallback&name=" + queryname.value)
document.body.appendChild(script)
}
AJAX基础
- POST
//获取元素对象
const result = document.getElementById("result");
//绑定事件
result.addEventListener("mouseover", function () {
// 1.创建对象
const xhr = new XMLHttpRequest();
// 2. 初始化 设置类型与URL 这是请求类型 和 请求类型 请求行
xhr.open('POST', 'http://127.0.0.1:8000/server');
// 3. 发送 这是请求体
xhr.send('a=100&b=200&c=300');
// 4.事件绑定
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300) {
//处理服务端返回的结果
result.innerHTML = xhr.response;
}
}
}
})
- GET
//获取button 元素
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?a=100&&b=200&&c=300');
// 3. 发送
xhr.send();
// 4.事件绑定 处理服务端返回的结果
// on when 当...的时候
// redaystate 是xhr对象中的属性, 表示状态 这个属性有五个值 0(为初始化) 1(表示open方法已经调用完毕) 2(表示send方法已经调用完毕) 3(表示服务端返回了部分的结果) 4(表示服务端返回了所有的结果)
// change 改变的时候触发
xhr.onreadystatechange = function () {
// 要状态为4的时候处理
if (xhr.readyState === 4) {
// 判断响应状态码 200(正确拿到服务端的东西) 404 403 401 500
// 2开头的都是表示成功
if (xhr.status >= 200 && xhr.status < 300) {
// 处理结果 行 头 空行 体
//空行没必要 这是应用层
// 1. 响应行 状态码 和 状态字符串
// console.log(xhr.status);//状态码
// console.log(xhr.statusText);//响应状态字符串
// console.log(xhr.getAllResponseHeaders);//所有的响应头
// console.log(xhr.response);//响应体
//设置result的文本
result.innerHTML = xhr.response;
}
}
}
}