标签:架构 apple document 说明 promise == open 随机 跳转
跨域是指一个域下的文档或脚本试图去请求另一个域下的资源,这里跨域是广义的。
广义的跨域:
<link>、<script>、<img>、<frame>等dom标签,还有样式中background:url()、@font-face()等文件外链
其实我们通常所说的跨域是狭义的,是由浏览器同源策略限制的一类请求场景。
还是上文的例子,例如:http://store.company.com/dir/page.html 请求以下地址的资源
URL | 结果 | 原因 |
---|---|---|
https://store.company.com/secure.html | 失败 | 不同协议 ( https 和 http ) |
http://store.company.com:81/dir/etc.html | 失败 | 不同端口 ( http:// 80是默认的) |
http://news.company.com/dir/other.html | 失败 | 不同域名 ( news 和 store ) |
失败的原因就是浏览器同源策略的限制,也就是所说的狭义的跨域
特别说明
第一:如果是协议和端口造成的跨域问题“前台”是无能为力的。
第二:在跨域问题上,仅仅是通过“URL的首部”来识别而不会根据域名对应的IP地址是否相同来判断。“URL的首部”可以理解为“协议, 域名和端口必须匹配”。
这里你或许有个疑问:请求跨域了,那么请求到底发出去没有?
跨域并不是请求发不出去,请求能发出去,服务端能收到请求并正常返回结果,只是结果被浏览器拦截了。你可能会疑问明明通过表单的方式可以发起跨域请求,为什么 Ajax 就不会?因为归根结底,跨域是为了阻止用户读取到另一个域名下的内容,Ajax 可以获取响应,浏览器认为这不安全,所以拦截了响应。但是表单并不会获取新的内容,所以可以发起跨域请求。同时也说明了跨域并不能完全阻止 CSRF,因为请求毕竟是发出去了。
这里引自浪里行舟
JSONP 由两部分组成:回调函数和数据。
JSONP原理
<script>
带有 src 属性可以跨域访问,网页可以得到从其他来源动态产生的 JSON 数据。JSONP请求需要请求资源所在服务器配合。JSONP 优缺点
当访问http://localhost:3000/11-jsonp.html
可以拿到https://www.runoob.com/try/ajax/jsonp.php?jsoncallback=callbackFunction
提供的数据,并进行处理。
备注:
callbackFunction(["customername1","customername2"])
准备工作
100-server1.js
let express = require(‘express‘);
let app = express();
app.use(express.static(__dirname));
app.listen(3000);
11-jsonp.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<script type="text/javascript">
var script = document.createElement(‘script‘);
script.type = ‘text/javascript‘;
// 传参一个回调函数名给后端,方便后端返回时执行这个在前端定义的回调函数
script.src = ‘https://www.runoob.com/try/ajax/jsonp.php?jsoncallback=callbackFunction‘;
document.head.appendChild(script);
// 回调执行函数
const callbackFunction = function(data) {
console.log(data)
}
</script>
</body>
</html>
运行:
npm install express
http://localhost:3000/11-jsonp.html
["customername1", "customername2"]
如图
解释:
var script = document.createElement(‘script‘);
script.type = ‘text/javascript‘;
script.src = ‘https://www.runoob.com/try/ajax/jsonp.php?jsoncallback=callbackFunction‘;
document.head.appendChild(script);
以上这段代码相当于在 <head> 标签内增加
<script type="text/javascript" src="https://www.runoob.com/try/ajax/jsonp.php?jsoncallback=callbackFunction"></script>
如图
<script>
带有 src 属性可以跨域访问,所以可以拿到 src 属性值所指的地址,拿到数据,并进行处理callbackFunction
可以自定义,需要创建与之相同的处理函数jsoncallback
是与后端商量好的接口新建 12-jsonp.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<script type="text/javascript">
// https://www.runoob.com/try/ajax/jsonp.php?jsoncallback=callbackFunction
function jsonp({
url,
jsoncallback
}) {
return new Promise((resolve, reject) => {
let script = document.createElement(‘script‘)
window[jsoncallback] = function(data) {
resolve(data)
document.body.removeChild(script)
}
script.src = `${url}?jsoncallback=${jsoncallback}`
document.body.appendChild(script)
})
}
jsonp({
url: ‘https://www.runoob.com/try/ajax/jsonp.php‘,
jsoncallback: ‘callbackFunction‘
}).then(data => {
console.log(data)
})
</script>
</body>
</html>
jsoncallback 如果一样, 会被覆盖掉;为了解决这个问题,可以随机产生函数名
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<script type="text/javascript">
// https://www.runoob.com/try/ajax/jsonp.php?jsoncallback=linXXXXX
function jsonp({
url,
jsoncallback
}) {
return new Promise((resolve, reject) => {
let script = document.createElement(‘script‘)
window[jsoncallback] = function(data) {
resolve(data)
document.body.removeChild(script)
}
script.src = `${url}?jsoncallback=${jsoncallback}`
console.log(script.src)
document.body.appendChild(script)
})
}
// 每次请求之前,产生一个随机的函数名
// 目的是,服务端接收到请求之后,返回一个 callbackName([JOSN格式的数据])
let callbackName = ‘lin‘ + Math.floor(Math.random() * 100000);
console.log(callbackName)
jsonp({
url: ‘https://www.runoob.com/try/ajax/jsonp.php‘,
jsoncallback: callbackName
}).then(data => {
console.log(data)
})
</script>
</body>
</html>
在这个例子中,对之前的流程进行一些改变。当访问http://localhost:3000/14-jsonp.html
可以拿到http://localhost:4001/say?wd=hello&jsoncallback=linXXXXX
提供的数据,并进行处理。
准备工作
14-jsonp.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<script type="text/javascript">
// http://localhost:4001/say?wd=hello&jsoncallback=linXXXXX
function jsonp({
url,
params,
jsoncallback
}) {
return new Promise((resolve, reject) => {
let script = document.createElement(‘script‘)
window[jsoncallback] = function(data) {
resolve(data)
document.body.removeChild(script)
}
if(params){
params = { ...params,
jsoncallback
} // wd=hello&jsoncallback=linXXXXX
let arrs = []
for (let key in params) {
arrs.push(`${key}=${params[key]}`)
}
script.src = `${url}?${arrs.join(‘&‘)}`
}else{
script.src = `${url}?jsoncallback=${jsoncallback}`
}
console.log(script.src)
document.body.appendChild(script)
})
}
// 每次请求之前,产生一个随机的函数名
// 目的是,服务端接收到请求之后,返回一个 callbackName([JOSN格式的数据])
let callbackName = ‘lin‘ + Math.floor(Math.random() * 100000);
console.log(callbackName)
jsonp({
url: ‘http://localhost:4001/say‘,
params: {
wd: ‘hello‘
},
jsoncallback: callbackName
}).then(data => {
console.log(data)
})
</script>
</body>
</html>
101-server2.js
let express = require(‘express‘)
let app = express()
app.get(‘/say‘, function(req, res) {
let {
wd,
jsoncallback
} = req.query
console.log(wd) // hello
console.log(jsoncallback) // linXXXXX
res.end(`${jsoncallback}(["customername1","customername2"])`)
})
app.listen(4001)
运行:
http://localhost:3000/14-jsonp.html
linXXXXX(["customername1","customername2"])
。CORS[Cross-Origin Resource Sharing] 是主流的跨域解决方案。目前,所有浏览器都支持该功能(IE8+:IE8/9需要使用 XDomainRequest 对象来支持CORS)。分为简单请求和非简单请求。
何为简单请求
简单请求的跨域访问
test
http://localhost:3000/4-index.html
页面,跨域访问http://localhost:4000/getData
,并拿到数据。代码实现
4-index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<script type="text/javascript">
let xhr = new XMLHttpRequest()
xhr.open(‘GET‘, ‘http://localhost:4000/getData‘, true)
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
console.log(xhr.response)
}
}
}
xhr.send()
</script>
</body>
</html>
4-server1.js
let express = require(‘express‘);
let app = express();
app.use(express.static(__dirname));
app.listen(3000);
4-server2.js
let express = require(‘express‘)
let app = express()
app.use(function(req, res, next) {
// 设置允许的域
res.setHeader(‘Access-Control-Allow-Origin‘, ‘http://localhost:3000‘)
next()
})
app.get(‘/getData‘, function(req, res) {
console.log(req.headers)
res.end(‘goodbye‘)
})
app.use(express.static(__dirname))
app.listen(4000)
node 4-server1.js
和 node 4-server2.js
http://localhost:3000/4-index.html
4-server2.js 运行终端中输出
{
host: ‘localhost:4000‘,
connection: ‘keep-alive‘,
‘user-agent‘: ‘Mozilla/5.0 (Windows NT 10.0; Win64; x64) ‘ +
‘AppleWebKit/537.36 (KHTML, like Gecko) ‘ +
‘Chrome/81.0.4044.138 Safari/537.36‘,
accept: ‘*/*‘,
origin: ‘http://localhost:3000‘,
‘sec-fetch-site‘: ‘same-site‘,
‘sec-fetch-mode‘: ‘cors‘,
‘sec-fetch-dest‘: ‘empty‘,
referer: ‘http://localhost:3000/4-index.html‘,
‘accept-encoding‘: ‘gzip, deflate, br‘,
‘accept-language‘: ‘zh-CN,zh;q=0.9,en;q=0.8‘
}
预检请求头部
预检响应头部
test
http://localhost:3000/5-index.html
页面,跨域访问http://localhost:4000/getData
,并拿到数据。代码实现
5-index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<script type="text/javascript">
let xhr = new XMLHttpRequest()
document.cookie = ‘name=lin‘ // cookie不能跨域
xhr.withCredentials = true // 前端设置是否带cookie
xhr.open(‘PUT‘, ‘http://localhost:4000/getData‘, true)
xhr.setRequestHeader(‘name‘, ‘lin‘)
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
console.log(xhr.response)
//得到响应头,后台需设置Access-Control-Expose-Headers
console.log(xhr.getResponseHeader(‘name‘))
}
}
}
xhr.send()
</script>
</body>
</html>
5-server1.js
let express = require(‘express‘);
let app = express();
app.use(express.static(__dirname));
app.listen(3000);
5-server2.js
let express = require(‘express‘)
let app = express()
let whitList = [‘http://localhost:3000‘] //设置白名单
app.use(function(req, res, next) {
let origin = req.headers.origin
if (whitList.includes(origin)) {
// 告知浏览器允许哪些域访问当前资源
res.setHeader(‘Access-Control-Allow-Origin‘, origin)
// 告知客户端后续请求允许携带的头部
res.setHeader(‘Access-Control-Allow-Headers‘, ‘name‘)
// 告知客户端后续请求允许使用的方法
res.setHeader(‘Access-Control-Allow-Methods‘, ‘PUT‘)
// 告知浏览器是否可以将 Credentials 暴露给客户端使用,Credentials 包含 cookie、authorization 类头部、 TLS证书等
res.setHeader(‘Access-Control-Allow-Credentials‘, true)
// 告知客户端该响应的信息可以缓存多久
res.setHeader(‘Access-Control-Max-Age‘, 6)
// 告知浏览器哪些响应头部可以供客户端使用
res.setHeader(‘Access-Control-Expose-Headers‘, ‘name‘)
if (req.method === ‘OPTIONS‘) {
res.end() // OPTIONS 请求不做任何处理
}
}
next()
})
app.put(‘/getData‘, function(req, res) {
console.log(req.headers)
res.setHeader(‘name‘, ‘js‘) //返回一个响应头,后台需设置
res.end(‘goodbye‘)
})
app.get(‘/getData‘, function(req, res) {
console.log(req.headers)
res.end(‘goodbye‘)
})
app.use(express.static(__dirname))
app.listen(4000)
node 5-server1.js
和 node 5-server2.js
http://localhost:3000/5-index.html
5-server2.js 运行终端中输出
{
host: ‘localhost:4000‘,
connection: ‘keep-alive‘,
‘content-length‘: ‘0‘,
name: ‘lin‘,
‘user-agent‘: ‘Mozilla/5.0 (Windows NT 10.0; Win64; x64) ‘ +
‘AppleWebKit/537.36 (KHTML, like Gecko) ‘ +
‘Chrome/81.0.4044.138 Safari/537.36‘,
accept: ‘*/*‘,
origin: ‘http://localhost:3000‘,
‘sec-fetch-site‘: ‘same-site‘,
‘sec-fetch-mode‘: ‘cors‘,
‘sec-fetch-dest‘: ‘empty‘,
referer: ‘http://localhost:3000/5-index.html‘,
‘accept-encoding‘: ‘gzip, deflate, br‘,
‘accept-language‘: ‘zh-CN,zh;q=0.9,en;q=0.8‘,
cookie: ‘name=lin‘
}
标签:架构 apple document 说明 promise == open 随机 跳转
原文地址:https://www.cnblogs.com/chrislinlin/p/12984425.html