Node 解决跨域问题 JSONP与CORS
今天浅谈一下Node解决跨域的问题,其实跨域这个问题因为同源策略而一直存在的,是前端的必经之路,原来一直在用CORS,对于JSONP都忘的差不多,今天遇到一些小问题居然也懵了圈,以前也没有写过关于这方面的博文,所以今天特地拿出来写一下,希望能帮到大家。
CORS 与 JSONP的区别
CORS全称为跨域资源共享”(Cross-origin resource sharing),对于用了这么久的CORS的我来说,那感觉肯定是很爽了,相对于JSONP来也有很多好处,比如最大的一个看点就是,JSONP只能用GET请求来跨域,而使用了CORS之后,GET和POST都能用,并且还支持其他的HTTP METHOD,并且可以用Access-Control-Allow-Method进行筛选。
以前做一个小项目,为了使前后端分离,特地在测试期间,把后端使用CORS,这样前端在自己的本地服务器上测试的时候,若需要数据方面的测试也能方便的从后端获取。JSONP却达不到这样的效果,请求方法各式各样,不可能把精力都放在跨域的地方。
JSONP
为了使安全而出现的同源策略给我们带来了众多的好处,但是同时也限制了我们跨域获取资源,凡事都有正反两面,但同源策略的出现带给我们的总是利大于弊。
JSONP就是在这种环境下出现的,它是一种非正式协议,使用JSONP跨域的方法大概是传给后端一个callback,后端收到后把,需要客户端需要的数据放到callback的参数列表中,这样客户端就能拿到了(这里的客户端一般是指服务器)。
但是刚刚不是才说由于同源策略的限制而不能跨域么?对的,但是有几个标签是不受同源策略的约束的,比如img, iframe, script,我们大可将它们利用起来,帮助我们实现跨域。
其实这里的callback并不是指的真正的回调函数,我们知道,回调函数是给调用者传入回调函数的函数指针,当调用者执行完毕后调用回调函数的回调指针,这就是一次回调,然而,JSONP只是传给后端一个回调函数名。
说了这么多,接下来来看一个小例子。
在这个简单的html页面中,我们嵌入了script标签,它的src指向一个地址,这里的地址为请求后端的接口(这里为了方便演示直接请求到了根目录,总所周知,这是一种很不好的url设计),并附带一个callback的参数为getName,getName就是我们的回调函数名。这里会对后端发起一个GET请求。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script>
function getName(data){
console.log(data);
}
</script>
<script src="http://localhost:9999?callback=getName"></script>
</body>
</html>
为了方便直接用Node作为后端了
const http = require('http');
const url = require("url");
http.createServer((req,res) => {
let params = url.parse(req.url,true);
if(params){
res.end(params.query.callback + `(${JSON.stringify({name:"zhangsan"})})`);
return;
}
res.end();
}).listen(9999,() => {
console.log("listening");
});
if块中书写拿到queryParams之后的操作,这里的查询字符串就是上面客户端传来的callback。
res.end(params.query.callback + `(${JSON.stringify({name:"zhangsan"})})`);
接着返回一个函数调用给客户端,实际上这里调用的就是客户端的getName()函数
启动服务器,客户端发起请求
如图所示已经拿到了数据。
综上,就是一次简单的JSONP。
JQuery的JSONP
今天就死在这上面了,已经被MVVM,MVC,MVP洗脑的我,已经对JQ陌生,今天用JQ发送AJAX时,发现JQ可以用POST请求发起一次JSONP跨域,以前也没怎么注意。下面来一下JQuery是怎么发起一次跨域请求。
一个完整的JQ AJAX形如:
$.ajax({
url:"http://localhost:9999",
data:{name:"zhangsan"},
type:"post",
dataType:"jsonp",
jsonp:"callback",
jsonpCallback:"getName",
async:true,
timeout:5000,
complete:function(){
console.log("end");
},
success:function(data){
console.log(data);
},
error:function(err){
console.log(`have a error is ${JSON.stringify(err)}`);
}
});
它与传统的AJAX并无两样,重点在于jsonp 和 jsonpCallback字段
jsonp 用于指定传入后端的callback名称的参数名,如上面用script标签发起跨域请求时,参数名就为callback,默认情况下,jsonp字段为callback。这里写出来只是为了让大家知道存在这么个字段。
jsonpCallback为需要传过去的回调函数名。但是这里并没有可以让我们调用的回调函数,这是因为jq的ajax使用jsonp时,获取到的数据已经作为参数传入success回调中,因此我们可以在success回调中拿到。
刷新页面,相同的结果!
jsonp无疑是一种很nice的跨域解决方案。
CORS
跨域资源共享(Cross-origin resource sharing)机制允许 Web 应用服务器进行跨域访问控制,从而使跨域数据传输得以安全进行。
如果说jsonp是前端跨域的利剑,那CORS就是可以让前端徒手空拳跨域的Buff,它的配置也相当简单。
重点就在于响应头,我们需要配置这么几个字段。
res.header("Access-Control-Allow-Origin","*");
res.header("Access-Control-Allow-Headers", "X-Requested-With,Origin,Content-Type,Accept");
res.header("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS");
下面一个一个来看:
第一个响应头:
Access-Control-Allow-Origin
这是为了设置允许跨域的客户端请求地址
第二个响应头:
res.header("Access-Control-Allow-Headers", "X-Requested-With,Origin,Content-Type,Accept");
响应首部 Access-Control-Allow-Headers 用于 preflight request(预检请求)中,列出了将会在正式请求的 Access-Control-Expose-Headers 字段中出现的首部信息。
在一些非简单请求中,会发起一次预检请求,比如下面这些HTTP METHOD会发起一个预检请求,检查服务器是否同意此请求对服务器资源进行修改。
PUT
DELETE
CONNECT
OPTIONS
TRACE
PATCH
第三个响应头:
res.header("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS");
这是为了设置是否同意这些方法来修改服务器资源。
总结
跨域是web开发中必不可少的技术,虽然很简单,但是也不可忽视。