内容简介:跨域是指不同协议、域名、端口下访问js脚本。而当遇到跨域时,由于浏览器中同源策略是指限制了脚本与不同源的资源交互,而当中的这里只总结关于AJAX方面的跨域问题。同源政策规定,AJAX请求只能发给同源的网址,否则就报错。而解决方案一般有
跨域是指不同协议、域名、端口下访问js脚本。而当遇到跨域时,由于浏览器中 同源策略 的安全限制,导致不能正常执行,报类似以下的错误:
同源策略
什么是同源策略
同源策略是指限制了脚本与不同源的资源交互,而当中的 源 是以协议、域名和端口区分,以下情况为不同源:
http://www.example.com/dir2/other.html:同源 http://example.com/dir/other.html:不同源(域名不同) http://v2.www.example.com/dir/other.html:不同源(域名不同) http://www.example.com:81/dir/other.html:不同源(端口不同) 复制代码
限制范围
(1) Cookie、LocalStorage 和 IndexDB 无法读取。 (2) DOM 无法获得。 (3) AJAX 请求不能发送。 复制代码
AJAX
这里只总结关于AJAX方面的跨域问题。同源政策规定,AJAX请求只能发给同源的网址,否则就报错。而解决方案一般有 JSONP
、 CORS
和 WebSocket
。
JSONP
JSONP是服务器与客户端跨源通信的常用方法,这里我们写一个例子去探究 JSONP
的原理。
JSONP的实现原理
首先,网页通过添加一个 <script>
元素,向服务器请求JSON数据,这种做法不受同源政策限制。
render.js: const express = require('express'); const app = express(); app.get('/jsonp', (req, res) => { const viewPath = `${__dirname}/jsonp.html`; res.setHeader('Content-Type', 'text/html'); res.sendFile(viewPath); }); app.listen(8081); console.log(`listen 8081....`); 复制代码
jsonp.html: <html> <body> <h1>jsonp test</h1> </body> <script> function addScriptsTag (src) { var script = document.createElement('script'); script.setAttribute('type', 'text/javascript'); script.src = src; document.body.appendChild(script); } window.onload = function () { console.log('onload'); addScriptsTag('http://127.0.0.1:8080/ip?callback=foo'); } function foo (data) { console.log(`Your public IP address is:${data.ip}`); } </script> </html> 复制代码
服务器收到请求后,将数据放在一个指定名字的回调函数里传回来。
const express = require('express'); const app = express(); app.get('/ip', (req, res) => { const callback = req.query.callback; res.send(`/**/ typeof ${callback} === 'function' && ${callback} ({"ip":"127.0.0.2"});`); // res.jsonp({ ip: '127.0.0.1' });// express自带jsonp更方便 }); app.listen(8080); console.log(`listen 8080....`); 复制代码
最后前端收到返回的数据如下所示,浏览器就会执行该脚本,调用前面定义好的回调函数 foo
。
/**/ typeof foo === 'function' && foo ({"ip":"127.0.0.2"}); 复制代码
使用
可以自行封装方法或者找第三方库去实现JSONP请求,比如jquery的jsonp方法。
CORS
CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。
CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会 自动添加一些附加的头信息 , 有时还会多出一次附加的请求 ,但用户不会有感觉。
两种请求
浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。
只要同时满足以下两大条件,就属于简单请求。
(1) 请求方法是以下三种方法之一: - HEAD - GET - POST (2)HTTP的头信息不超出以下几种字段: Accept Accept-Language Content-Language Last-Event-ID Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain 复制代码
凡是不同时满足上面两个条件,就属于非简单请求
简单请求
输入 127.0.0.1:8081/cors
,浏览器正常进行一次简单的AJAX请求(使用axios):
render.js const express = require('express'); const app = express(); app.get('/cors', (req, res) => { const viewPath = `${__dirname}/cros.html`; res.setHeader('Content-Type', 'text/html'); res.sendFile(viewPath); }); app.listen(8081); console.log(`listen 8081....`); 复制代码
cros.html: <html> <body> <h1>cors test</h1> <script src="https://unpkg.com/axios/dist/axios.min.js"></script> <script> axios({ method: 'post', baseURL: 'http://127.0.0.1:8080/', url: '/user', headers: { 'Content-Type': 'application/x-www-form-urlencoded', },// 默认是applictaion/json,为非简单请求,所以为了测试需自定义 data: { firstName: 'Fred', lastName: 'Flintstone' }, }) .then(function (response) { console.log(response); }) .catch(function (error) { console.log(error); }); </script> </body> </html> 复制代码
服务器通过中间件形式加上头部信息:
const express = require('express'); const app = express(); app.all('*', function(req, res, next) { res.header("Access-Control-Allow-Origin", "http://127.0.0.1:8081"); next(); }); app.post('/user', (req, res) => { res.send('user ok'); }); app.listen(8080); console.log(`listen 8080....`); 复制代码
这时候CORS就成功了,通信过程的头部信息:
请求头:
POST http://127.0.0.1:8080/user HTTP/1.1 Host: 127.0.0.1:8080 Connection: keep-alive Content-Length: 44 Accept: application/json, text/plain, */* Origin: http://127.0.0.1:8081 ... {"firstName":"Fred","lastName":"Flintstone"} 复制代码
响应头:
HTTP/1.1 200 OK X-Powered-By: Express Access-Control-Allow-Origin: http://127.0.0.1:8081 ... user ok 复制代码
其中起关键作用的是 Origin
和 Access-Control-Allow-Origin
, Origin
浏览器请求时会带上,指明请求的来源。而响应头的 Access-Control-Allow-Origin
指明能够跨域请求资源的允许网址,多个用 逗号 隔开,如果写* 号 表明任意网址都可以。
非简单请求
当请求为非简单请求时,一次请求会分两次请求,分别是预检请求和正常的请求。
我们现在把上面AJAX请求方法变为 PUT
,
预检请求
请求就会先发一次预检请求。请求头如下:
OPTIONS http://127.0.0.1:8080/user HTTP/1.1 Host: 127.0.0.1:8080 Connection: keep-alive Access-Control-Request-Method: PUT Origin: http://127.0.0.1:8081 ... 复制代码
预检请求的方法为 OPTIONS
,表明请求时用来询问的。 Origin
依然表明请求来源。而新增的请求字段 Access-Control-Request-Method
表明接下来的CORS请求会到什么方法,能否使用。
这时服务器通过设置 Access-Control-Allow-Methods
来规定服务器支持的CORS请求方法:
res.header("Access-Control-Allow-Methods","PUT,POST,GET,DELETE"); 复制代码
正确返回后,响应头如下:
HTTP/1.1 200 OK X-Powered-By: Express Access-Control-Allow-Origin: http://127.0.0.1:8081 Access-Control-Allow-Methods: PUT,POST,GET,DELETE Allow: PUT ... 复制代码
正常请求
正常请求的请求头:
PUT http://127.0.0.1:8080/user HTTP/1.1 Host: 127.0.0.1:8080 Connection: keep-alive Content-Length: 44 Accept: application/json, text/plain, */* Origin: http://127.0.0.1:8081 ... {"firstName":"Fred","lastName":"Flintstone"} 复制代码
回应:
HTTP/1.1 200 OK X-Powered-By: Express Access-Control-Allow-Origin: http://127.0.0.1:8081 Access-Control-Allow-Methods: PUT,POST,GET,DELETE ... user ok 复制代码
和简单请求一样,请求写到 Origin
,回包携带 Access-Control-Allow-Origin
请求时支持携带Cookie
客户端, XMLHttpRequest
对象开启 withCredentials
属性。
JS写法:
var xhr = new XMLHttpRequest(); xhr.withCredentials = true; 复制代码
axios开启选项即可:
withCredentials: true, 复制代码
服务端同意允许发送Cookie,通过设置头部:
res.header("Access-Control-Allow-Credentials",true); 复制代码
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。