# 通过script进行跨域(Jsonp)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<button onclick="useScript()">click</button>
<button onclick="noScript()">click2</button>
<script src="http://code.jquery.com/jquery-latest.js"></script>
<script>
function useScript () {
console.log(1)
var script = document.createElement('script')
script.src = 'http://localhost:3000/test?callback=show'
document.body.appendChild(script)
}
function noScript () {
$.get("http://localhost:3000/test", function (result) {
console.log('click2')
})
}
function show () {
console.log('show')
}
</script>
</body>
</html>
const express = require('express')
const bodyParser = require('body-parser')
const app = express()
app.use(bodyParser.json())
app.get('/test', (req,res) => {
console.log(req.query.callback)
console.log(`${req.query.callback}()`)
res.send(`${req.query.callback}()`)
})
app.listen(3000, () => {
console.log('App listening on port 3000!')
})
点击click,终端会显示show;点击click2,终端会报跨域错误。script标签不受浏览器同源策略限制,但只能发起get请求,因为是通过script标签发出的请求,浏览器会将返回来的字符串按照javascript进行解析执行,在这个例子中,我们返回的字符串是"show()",则浏览器会调用我们定义的show函数,因此终端会出现show。
# 通过irame加form进行跨域请求
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<button onclick="get()">get</button>
<button onclick="getByScript()">getByScript</button>
<button onclick="postByIFrame()">postByIFrame</button>
<script src="http://code.jquery.com/jquery-latest.js"></script>
<script>
function get () {
console.log(1)
var script = document.createElement('script')
script.src = 'http://localhost:3000/test?callback=show'
document.body.appendChild(script)
}
function getByScript () {
$.get("http://localhost:3000/test", function (result) {
console.log('click2')
})
}
function postByIFrame () {
const iframe = document.createElement('iframe')
iframe.name = 'iframePost'
iframe.style.display = 'none'
document.body.appendChild(iframe)
const form = document.createElement('form')
iframe.addEventListener('load', (res) => {
console.log(res)
})
form.action = 'http://localhost:3000/test'
form.target = iframe.name
form.method = 'post'
form.style.display = 'none'
document.body.appendChild(form)
form.submit()
document.body.removeChild(form)
}
function show () {
console.log('show')
}
</script>
</body>
</html>
const express = require('express')
const bodyParser = require('body-parser')
const app = express()
app.use(bodyParser.json())
app.get('/test', (req,res) => {
console.log(req.query.callback)
console.log(`${req.query.callback}()`)
res.send(`${req.query.callback}()`)
})
app.post('/test', (req, res) => {
console.log('test post')
res.end()
})
app.listen(3000, () => {
console.log('App listening on port 3000!')
})
点击postByIFrame按钮,服务器终端会显示test post,浏览器终端执行iframe.addEventListener的回调函数,说明通过iframe加上form,可以进行post请求,解决了script标签只能进行get请求的问题。
# Cors(跨域资源共享标准)
因为浏览器的同源策略存在,XMLHttpRequest发起跨域请求时,会收到跨域资源共享标准的限制。
跨域资源共享标准允许通过控制一些HTTP头部字段来允许跨域。
与跨域相关的头部信息:
Access-Controll-Allow-Origin
这个头部信息对应的值可以是“*”可以是任意指定IP
- “*”是允许所有IP进行跨域请求
- 指定IP,例如localhost:8080,就只有来自localhost:8080的请求才会被处理
考虑这样一种情况,服务器只希望响应a.com和b.com的请求,拒绝c.com的请求,那么Access-Control-Allow-Origin
的值是不能在后面接上多个IP的,例如:Access-Control-Allow-Origin: a.com, b.com
这种写法是不起作用的。要解决这个问题,可以利用请求头中的origin
字段,origin
是客户端发起请求时自动带上的,它的值是发起请求一方的域,可以参考以下代码:
// 该服务端代码使用koa框架
const whiteList = ['a.com', 'b.com']
const isAllowed = (origin) => {
return whiteList.indexOf(origin) !== -1
}
app.use(async (ctx, next) => {
const origin = ctx.request.header.origin
if (isAllowed(origin)) {
ctx.set('Access-Control-Allow-Origin', origin)
await next()
}
})
这段代码的意思就是,先用一个变量whiteList来保存我服务端希望响应的ip,当请求过来之后,判断请求头的origin是否在whiteList里,如果在就在响应头中设置Access-Control-Allow-Origin
的值为该origin,否则不做任何处理。当浏览器收到响应之后,它会检查是否有Access-Control-Allow-Origin
字段,如果没有就说明服务器不支持该域的跨域请求。
# 带身分凭证
当Access-Control-Allow-Origin: *
时,Access-Control-Allow-Credentials
不能设置为true,虽然发送方仍能发送请求并且带上cookie(客户端设置了credentials为true),浏览器也能够正常收到响应,但是并不会将响应交给客户端。
# 参考资料:
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS
https://www.jb51.net/article/109725.htm
# 代理
nginx配置
server{
# 监听9099端口
listen 9099;
# 域名是localhost
server_name localhost;
#凡是localhost:9099/api这个样子的,都转发到真正的服务端地址http://localhost:9871
location ^~ /api {
proxy_pass http://localhost:9871;
}
}
← 规范commit信息和lint代码 闭包 →