1 var http = require('http'); 2 3 var fs = require('fs'); 4 5 var url = require('url'); 6 7 8 9 // 创建服务器10 11 http.createServer( function (request, response) {12 13 // 解析请求,包括文件名14 15 var pathname = url.parse(request.url).pathname;16 17 // 输出请求的文件名18 19 console.log("Request for " + pathname + " received.");20 21 // 读取请求的文件内容22 23 fs.readFile(pathname.substr(1), function (err, data) {24 25 if (err) {26 27 console.log(err);28 29 // HTTP 状态码: 404 : NOT FOUND Content Type: text/plain30 31 response.writeHead(404, {'Content-Type': 'text/html'});32 33 }else{34 35 // HTTP 状态码: 200 : OK36 37 response.writeHead(200, {'Content-Type': 'text/html'});38 39 // 响应文件内容40 41 response.write(data.toString());42 43 }44 45 // 发送响应数据46 47 response.end();48 49 });50 51 }).listen(8082);52 53 // 控制台会输出以下信息54 55 console.log('Server running at server.js: 1 var express = require('express'); 2 3 var app = express(); 4 5 var router = express.Router(); 6 7 8 9 router.all('/getData', function(req, res, next) {10 11 //设置允许跨域请求12 13 var reqOrigin = req.header("origin");14 15 console.log(reqOrigin);16 17 if(reqOrigin !=undefined &&18 19 reqOrigin.indexOf("http://u.58.com") > -1){20 21 //设置允许 这个域响应22 23 res.header("Access-Control-Allow-Origin", "http://u.58.com");24 25 res.header("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS");26 27 res.header("Authorization",'zhuanzhuanFe')28 29 res.header("Access-Control-Allow-Headers", "Content-Type,Content-Length, Authorization, Accept,X-Requested-With");30 31 }32 33 res.json(200, {name:"转转熊",property:"Cute"});34 35 });36 37 38 39 app.use('/', router);40 41 42 43 var server = app.listen(8081, function () {44 45 console.log("应用实例,访问地址 >);46 47 });48 49 50 51 console.log('Server running at >
1 <!DOCTYPE html> 2 3 <html lang="zh-CN"> 4 5 <head> 6 7 <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> 8 9 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">10 11 <title>test cors</title>12 13 </head>14 15 <body >16 17 <button id="btn" onclick="getData()">跨域获取数据</button>18 19 </body>20 21 <script>22 23 function getData(){24 25 var xhr = new 26 27 xhr.onreadystatechange = function () {28 29 if(xhr.readyState == 4){30 31 if(xhr.status >= 200 && xhr.status < 300 || xhr.status == 304){32 33 console.log(xhr.responseText)34 35 }else {36 37 console.log('err' + xhr.status);38 39 }40 41 }42 43 };44 45 xhr.open('get','http://m.zhuanzhuan.com/getData',true);46 47 xhr.send(null);48 49 }50 51 52 53 </script>54 55 </html>
我们在控制台执行我们创建的client.js
在浏览器访问 src='/images/loading.gif' data-original="https://mmbiz.qpic.cn/mmbiz_png/T81bAV0NNNibbwWrLf7JVgGOlRMp3iaZbO9TxotMbBp4icibjSjsjYnukcULbP90EcfX6HibPVVTephKicNVkPM7FkyQ/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1" data-s="300,640" data-type="png" data-src='/images/loading.gif' data-original="https://mmbiz.qpic.cn/mmbiz_png/T81bAV0NNNibbwWrLf7JVgGOlRMp3iaZbO9TxotMbBp4icibjSjsjYnukcULbP90EcfX6HibPVVTephKicNVkPM7FkyQ/0?wx_fmt=png" data-ratio="0.1642651296829971" data-w="347" data-fail="0">
同时,我们把我们的服务端也跑起来,注意上一个服务不要停掉哈
有了代码心里就有底了,接下来我们来看下简单和非简单请求,CORS到底do了what!
1.简单请求:
按照上面的getData方法的配置:
xhr.open('get','http://m.zhuanzhuan.com/getData',true);
我们点击一 src='/images/loading.gif' data-original="https://mmbiz.qpic.cn/mmbiz_png/T81bAV0NNNibbwWrLf7JVgGOlRMp3iaZbOIEVdltlRm17FwEUNibWrb6WiaG9BOwE1sxeJsy4Z2MHAEar8qNhPf9UQ/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1" data-s="300,640" data-type="png" data-src='/images/loading.gif' data-original="https://mmbiz.qpic.cn/mmbiz_png/T81bAV0NNNibbwWrLf7JVgGOlRMp3iaZbOIEVdltlRm17FwEUNibWrb6WiaG9BOwE1sxeJsy4Z2MHAEar8qNhPf9UQ/0?wx_fmt=png" data-ratio="0.39264264264264265" data-w="1332" data-fail="0">
请求头RequestHeaders中有一个Origin字段,这个字段表示本次请求来自哪个源(协议 + 域名 + 端口)
服务器接收到请求后,如果不为这个跨域请求做任何的操作,如下改写下server.js:
router.all('/getData', function(req, res, next) {
//不做任何的处理
});
服务器接收到请求后,如果Origin指定的源不在许可范围内,服务器会返回一个正常的HTTP回应,浏览器接收到的回应头信息中没有包含Access-Control-Allow-Origin字段,那么浏览器就会抛出一个错误,被XHR的onerror函数捕捉,这种情况无法通过状态码判断,状态码可能会返回200。
如果请求头中的Origin字段是服务器允许的来源,那么服务器会在请求的返回头中添加Access-Control-Allow-Origin字段,并赋值为请求头中的Origin,表示允许该源请求资源。接下来我们恢复server.js中之前对getData请求的处理,再次点击按钮发起跨域请求,模拟一下上述情景:
1 router.all('/getData', function(req, res, next) { 2 3 //设置允许跨域请求 4 5 var reqOrigin = req.header("origin"); 6 7 console.log(reqOrigin); 8 9 if(reqOrigin !=undefined && reqOrigin.indexOf("http://u.58.com") > -1){10 11 //设置允许 这个域响应12 13 res.header("Access-Control-Allow-Origin", "http://u.58.com");14 15 res.header("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS");16 17 res.header("Authorization",'zhuanzhuanFe');18 19 res.header("Access-Control-Allow-Headers", "Content-Type,Content-Length, Authorization");20 21 }22 23 res.json(200, {name:"转转熊",property:"Cute"});24 25 });
如果Origin指定的域名在许可的范围内的话,服务器返回的响应,会多出来几个头信息字段:
响应头Response Headers中以Access-Control-开头的字段都是与CORS相关的字段,其中Access-Control-Allow-Origin 该字段是允许跨域响应头中必不可少的一个字段,值一般都是请求中Origin的值,表示允许当前源的跨域请求,也可以设置成*,表示接受任意源的请求。其余相关字段下面会详解。
2.非简单请求 Preflighted Request: 当浏览器发送的请求为非简单请求时,浏览器必须首先使用 OPTIONS 方法向服务器发起一个预检请求(Rreflighted Request),从而获知服务端是否允许该跨域请求。预检请求的使用,可以避免跨域请求对服务器的用户数据产生未预期的影响。
我们将请求从简单请求GET改成非简单请求PUT,将index.html中getData方法稍加改动:
xhr.open('put','http://m.zhuanzhuan.com/getData',true);
当然,我们还是要先看看对跨域请求不做任何处理的时候的状态,继续将server.js中对Access-Control-Allow-Origin字段的设置注释掉,发送下请求:
响应头中Access-Control-Allow-Origin字段如果不设置为Origin字段的值或者“*”,就表示该预检请求不被同意,会返回一个正常的HTTP回应,但是没有任何的CORS相关头信息字段,这时浏览器接收到响应,会被
我们接下来看看正常的复杂请求的处理逻辑。恢复server.js中对Access-Control-Allow-Origin字段的设置,刷新页面,点击按钮再次发送请求,我们看到比起我们要发送的PUT请求,页面多了一次类型为OPTIONS的请求:
这个就是预检请求。对于预检请求,响应头中有一个必须的字段:Access-Control-Request-Method,表示请求允许使用的方法,如果没有这个字段,预检请求无法通过,我们来实践一下,注释掉之前在响应中对Access-Control-Request-Method字段的设置,改写下server.js:
1 router.all('/getData', function(req, res, next) { 2 3 //设置允许跨域请求 4 5 var reqOrigin = req.header("origin"); 6 7 if(reqOrigin !=undefined && reqOrigin.indexOf("http://u.58.com") > -1){ 8 9 //设置允许 这个域响应10 11 res.header("Access-Control-Allow-Origin", "http://u.58.com");12 13 }14 15 res.json(200, {name:"转转熊",property:"Cute"});16 17 });
点击页面按钮发送put请求,可以看到页面控制台报错:
顾名思义,我们发送的put方法并没有在响应头中设置被允许,所以,预检请求失败,真正的请求也就被扼杀在摇篮里了...
来来来让我们动气程序员证就世界的手指把摇篮里的巨婴救活吧!恢复server.js到最开始的状态,重新发送请求:
服务器一旦通过了预检请求,以后每次浏览器正常的CORS请求都会跟简单请求一样,妥了,救活,万岁!
“凭证不是你想带,想带就能带”
默认情况下跨域请求不提供凭据(Cookie,HTTP认证以及SSL证明等),但是通过将xhr的withCredentials属性设置为true,就可以指定某个请求发送凭据。如果服务器接受带凭据的请求,会在响应头中用Access-Control-Allow-Credentials:true来响应。
首先我们改写下index.html中的getData方法:
1 function getData(){ 2 3 var xhr = new 4 5 //允许跨域携带请求 6 7 xhr.withCredentials = true; 8 9 xhr.onreadystatechange = function () {10 11 if(xhr.readyState == 4){12 13 if(xhr.status >= 200 && xhr.status < 300 || xhr.status == 304){14 15 console.log(xhr.responseText)16 17 }else {18 19 console.log('err' + xhr.status);20 21 }22 23 }24 25 };26 27 xhr.open('put','http://m.zhuanzhuan.com/getData',true);28 29 xhr.send(null);30 31 }
我们刷新页面发送请求:
一厢情愿了吧,意思就是在预检请求的响应头中如果不同时为Access-Control-Allow-Credentials设置为true,那么浏览器就不会把响应交给javascript,responseText为空字符串,status为0,触发onerror(),跨域携带凭证的请求是不被允许滴。
我们乖乖在server.js响应中加上下面的代码:
res.header("Access-Control-Allow-Credentials",'true');
再次请求,可以看到:
拖家带口闯关东成功!
对于附带身份凭证的请求,服务器不得设置 Access-Control-Allow-Origin 的值为“*”,值必须为Origin 首部字段所指明的域名即允许附带凭证的源,实践一下看看,将server.js中稍加修改:
res.header("Access-Control-Allow-Origin", "*");
发送请求,可以看到:
祖国河山一片红,不说应该也懂了吧,就是不行哈不行,NOOO!
“...我编不出来了...反正就是介绍下双方出场队员= =”
最后我们将跨域涉及到的一些响应头中Access-Control-Allow家族常见的字段罗列下,给我们的知识系统来几下80块的扎实大锤:
HTTP 响应首部字段
Access-Control-Allow-Origin: |*: 表示可以请求数据的请求来源
Access-Control-Expose-Headers:zhuanzhuanFe: 在跨域访问时,
Access-Control-Allow-Credentials: true:允许跨域携带凭证的字段
Access-Control-Max-Age: < delta-seconds >:来指定本次预检请求的有效期,单位为秒,在此期间浏览器无需为同一请求再次发送预检请求。请注意,浏览器自身维护了一个最大有效时间,如果该首部字段的值超过了最大有效时间,将不会生效。
Access-Control-Allow-Methods: < method >[, < method >]:首部字段用于预检请求的响应。其指明了实际请求所允许使用的 HTTP 方法。
Access-Control-Allow-Headers: < field-name >[, < field-name >]*:如果浏览器请求包括Access-Control-Request-Headers字段,则Access-Control-Allow-Headers字段是必需的。它也是一个逗号分隔的字符串,表明服务器支持的所有头信息字段,不限于浏览器在"预检"中请求的字段。
HTTP 请求首部字段
Origin: < origin >:表明预检请求或实际请求的源
Access-Control-Request-Method: < method >: 将实际请求所使用的 HTTP 方法告诉服务器。
Access-Control-Request-Headers: < field-name >[, < field-name >]*:将实际请求所携带的首部字段告诉服务器
差不多就这么多了,鞠躬感谢,前端小新人的一些拙劣总结,有问题的地方还希望各位前辈们多多指教,一起携手在前端的草原策马奔腾吧!嘚驾~~~~
如果你喜欢我们的文章,关注我们的公众号和我们互动吧。
原标题:CORS跨域资源共享你该知道的事儿
关键词: