# 通过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;
    }    
}