原文地址:
了解几个跨域的方案,并且通过简单实践进行体会。
如何实践?
但是,我们如何进行实践呢?在哪发请求?向什么服务器发请求?很简单,就在当前网页,打开控制台,输入请求的代码
var url = 'http://127.0.0.1:8888/';var xhr = new XMLHttpRequest();xhr.open('GET', url, true);xhr.send();
那么我们就可以以当前页面url作为origin,向:8888/ ,发送请求GET请求了。
同时在本地创建一个node服务var http = require('http');http.createServer(function (request, response) { response.writeHead(200, { 'Content-Type': 'text/plain' }); response.end('request success!!!');}).listen(8888);console.log('Server running at http://127.0.0.1:8888/');
这样我们就有服务器了,你可以很轻松的跟着这遍文章来实践了,然后从当前网页发送get请求到本地服务,理所当然跨域了。
ps: github网站不行(本文最初再github上编写),会引发csp错误,此错误是用于防止内容注入攻击的,不得不说,大网站安全措施做得就是好,转战segmentfault做实践。
1. cors
cors(跨域资源共享 Cross-origin resource sharing),它允许浏览器向跨域服务器发出XMLHttpRequest请求,从而克服跨域问题,它需要浏览器和服务器的同时支持。
cors 分为两种请求,简单请求和非简单请求,关于cors的更详细介绍,推荐阮一峰老师的 ,本文注重实践。
简单请求
正如上方的例子便是一个简单请求
var url = 'http://127.0.0.1:8888/';var xhr = new XMLHttpRequest();xhr.open('GET', url, true);xhr.send();
如何解决此案例的跨域问题呢?
浏览器端,浏览器会自动在请求头中添加 origin 字段,我们不需要操作。Request Headers: Origin: https://github.com
服务端,Access-Control-Allow-Origin属性,我们需要服务端设置此属性,指定允许的请求源域名,可以通过指定为 *
来指定所以域名。后端动起来:
var http = require('http');http.createServer(function (request, response) { response.writeHead(200, { 'Content-Type': 'text/plain', 'Access-Control-Allow-Origin': '*' }); response.end('request success!!!');}).listen(8888);console.log('Server running at http://127.0.0.1:8888/');
重启服务,再尝试
这次没有再报错了,我们看看服务器放回了什么nice!跨域成功!
非简单请求
同样我们在控制台输入一下代码进行put(非简单请求)
var url = 'http://127.0.0.1:8888/';var xhr = new XMLHttpRequest();xhr.open('PUT', url, true);xhr.send();
毫无意外的报错
在进行非简单请求的时候,浏览器会先发送一次OPTION请求来“预检”(preflight)该请求是否被允许,请求头中会通过Access-Control-Request-Method
,Access-Control-Request-Headers
来告诉服务器我需要用到的方法和字段,服务器通过返回的头部信息中的Access-Control-Allow-Origin
,Access-Control-Allow-Method
来告诉浏览器该跨域请求是否被允许。修改后端代码:
var http = require('http');http.createServer(function (request, response) { response.writeHead(200, { 'Content-Type': 'text/plain', 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET, POST, PUT' }); response.end('request success!!!');}).listen(8888);console.log('Server running at http://127.0.0.1:8888/');
可以看到浏览器会先发送一个预检
当确认允许跨域之后,以后再发送该请求,就会省去预检处理,之间当作简单请求来操作。很明显,修改了后端代码后,这次的put请求时成功的。这里就不继续上图了。
cors总结
cors(跨域资源共享 Cross-origin resource sharing),它允许浏览器向跨域服务器发出XMLHttpRequest请求,从而克服跨域问题,它需要浏览器和服务器的同时支持。
- 浏览器端会自动向请求头添加origin字段,表明当前请求来源。
- 浏览器端需要设置响应头的
Access-Control-Allow-Methods
,Access-Control-Allow-Headers
,Access-Control-Allow-Origin
等字段,指定允许的方法,头部,源等信息。 - 请求分为简单请求和非简单请求,非简单请求会先进行一次OPTION方法进行预检,看是否允许当前跨域请求。
2. jsonp
jsonp的原理就是利用就是利用script
标签没有跨域限制,可以通过script
标签的src属性发送GET
请求。我们继续尝试,先把后端有关跨域的设置去掉,并重启服务
var http = require('http');http.createServer(function (request, response) { response.writeHead(200, { 'Content-Type': 'text/plain' }); response.end('request success!!!');}).listen(8888);console.log('Server running at http://127.0.0.1:8888/');
打开我们的控制台输入一下代码,利用script标签进行jsonp请求
var script = document.createElement('script');script.type = 'text/javascript';script.src = `http://127.0.0.1:8888/`;document.head.appendChild(script);
可以看到,后端正常的返回了
request success !!!
而且该请求为GET
请求
Request URL: http://127.0.0.1:8888/Request Method: GETStatus Code: 200 OKRemote Address: 127.0.0.1:8888Referrer Policy: no-referrer-when-downgrade
但是我们现在只是成功发送了一个跨域请求,但是我们不像XMLHttpRequest那样可以在res.responseText中拿到数据,通过jsonp我们该怎么拿到请求的数据呢?方法就是前后端约定一个callback字段名,来传递函数名,前端通过该函数来拿到数据。前端代码修改为:
var script = document.createElement('script');script.type = 'text/javascript';script.src = `http://127.0.0.1:8888/?callback=onBack`;document.head.appendChild(script);function onBack (res) { console.log(JSON.stringify(res)); // 请求完后删除添加到页面上的script标签 var head = document.head head.removeChild(script)}
通过callback字段来传递函数名onBack,后端代码修改为
var http = require('http')var urlTool = require('url')// json 数据var data = {'methods': 'jsonp', 'result': 'success'};http.createServer(function (request, response) { var params = urlTool.parse(request.url, true) console.log(params) response.writeHead(200, { 'Content-Type': 'text/plain' }); if (params.query && params.query.callback) { // callback(data) var str = `${params.query.callback}(${JSON.stringify(data)})` } response.end(str);}).listen(8888);console.log('Server running at http://127.0.0.1:8888/');
重启后端服务,并且在控制台输入代码,可以看到结果:
我们拿到了数据,并且通过onBack函数将他输出到了控制台上!
总结
- jsonp是一种跨域方案,他利用
script
标签没有跨域限制的特点,通过script
标签的的src属性发送GET
请求。 - 可以通过前后端约定一个字段名,比如callback,来传递一个函数名,从而使得前端可以使用对应的callback函数,拿到数据,处理数据。
jsonp和cors比较
- CORS与JSONP的使用目的相同,但是比JSONP更强大。
- JSONP只支持GET请求,CORS支持所有类型的HTTP请求。JSONP的优势在于支持老式浏览器,以及可以向不支持CORS的网站请求数据。
同源策略:同源策略限制了一个源(origin)中加载文本或脚本与来自其它源(origin)中资源的交互方式,这是一个用于隔离潜在恶意文件的重要安全机制。如果两个页面拥有 相同 的 协议(protocol),端口(如果指定),和 主机,那么这两个页面就属于同一个源(origin)。