跨域解决方法(一)-JSONP

411 阅读4分钟

为什么会产生跨域

http://localhost:8080

对于一个请求来说,请求必定包括协议如http,域名如localhost,以及端口号8080这三部分

同源策略:同源意味着请求的源路径和目标路径所对应的协议域名端口号都是一样的,否则请求就是非同源的,同源策略是现在浏览器支持的一种策略,浏览器在正常情况下,是不允许不同源的资源相互请求访问,之所以浏览器这样限制,也是为了网站的安全着想,如果没有同源限制,则浏览器中的cookie可以任意读取,网站的数据就可以相互访问,这样是就很暴露隐私以及威胁网站安全的。

跨域:因为有了同源策略,我们不能随别获取一些不同源但是需要的资源,这就需要跨域,来解决浏览器中同源策略的问题。

一个简单的浏览器同源策略实例

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title>无标题文档</title>
</head>
<body>
<script>
    var xmlHttpRequest =new XMLHttpRequest();
    xmlHttpRequest.onreadystatechange=function(){
        if(xmlHttpRequest.readyState==4 && xmlHttpRequest.status==200){
            console.log(xmlHttpRequest.responseText);
        }
    }
    xmlHttpRequest.open("get","http://localhost:5000/list",true);
    xmlHttpRequest.send();
</script>
</body>
</html>

上面是一个原生的ajax请求,请求源路径为http://localhost:63342/webpackProjectTest/index.html?_ijt=irg82949qkhqr15ov5b6b4e190,但是目标路径端口号为5000,端口号不同,这违背了浏览器的同源策略,当运行html的时候,浏览器控制台会报下面的错误:

image-20200505172301459

这是一种跨域请求,我们要解决这种浏览器的限制,也就是实现跨域。

原生js实现jsonp跨域以及原理

服务段代码:使用node+express搭建,启动服务端口为5000,

let express = require('express');
let app = new express();
app.get("/list",function(req,res) {
    console.log(req.query);
    let {callback}=req.query;
    console.log(callback)
    let data={
        "name":"sunshine",
        "age":23
    }
    res.send(`${callback}(${JSON.stringify(data)})`);
});
app.listen(5000,function() {
    console.log("服务器启动成功!!");
})

客户端代码:使用原生js实现jsonp

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title>无标题文档</title>
</head>
<body>
<script>
    function success(data){
        console.log("从服务器获取数据:"+JSON.stringify(data));
    }
    let scriptDom=document.createElement('script');
    scriptDom.src="http://localhost:5000/list?callback=success";
    document.getElementsByTagName('body')[0].appendChild(scriptDom);
</script>
</body>
</html>

浏览器访问结果

从服务器获取数据:{"name":"sunshine","age":23}

jsonp实现原理

原生js中的script,link,img,iframe等资源访问标签自带跨域功能,也就是使用这些标签请求不同源的资源时,浏览器会默认放行。

jsonp就是利用scipt标签跨域功能,将跨域资源路径写在script中的src种进行跨域请求,scriptDom.src="http://localhost:5000/list?callback=success";请求路径后面定义了一个回调函数,随请求一并传给服务器,服务器会获取到这个参数,然后返回一个调用字符串,浏览器拿到字符串之后,会将字符串当成js代码进行执行,这就完成了跨域。

jsonp需要客户端和服务端的协作完成,客户端要定义全局的回调函数,并在请求参数中添加对应的回调函数。

服务端则需要获取到回调的函数,然后将服务数据放在回调函数中,作为字符串进行返回。

注意:浏览器有自动把服务器返回的字符串进行解析执行的能力

jquery实现jsonp跨域

服务端的设置没有改变,现在客户端使用jquery的ajax实现jsonp的请求,

<script>
    $.ajax({
        url: 'http://localhost:5000/list', //不同的域
        type: 'GET', // jsonp模式只有GET 是合法的
        dataType: 'jsonp', // 数据类型
        success:function(data){
            console.log("服务器返回数据:"+JSON.stringify(data));
        }
    })
</script>

jquery的ajax实现的jsonp底层原理也就是利用script标签的自带跨域,以及底层回调函数的封装。

总结由于jsonp是用script标签实现的一种跨域,那么jsonp只能实现get方式的请求,因为这些资源标签是不能实现get请求的,这也是jsonp的一大缺点,jsonp的第二大缺点是,get请求会造成请求资源暴露,所以jsonp是一种相对不安全的请求方式。这种方式以前很受欢迎,现在已经逐渐退出,由更优秀的跨域方案进行代替。但jsonp实现的思想还是值得前端开发人员去学习的。