跨域原理(一) JSONP

跨域与同源策略:

什么是跨域?

跨域 :跨域访问问题指的是在客户端浏览器中,由于同源策略的限制,不允许从一个源直接访问另一个源的资源。当浏览器发起一个跨域请求时,会被浏览器拦截,并阻止数据的传输。

如上图,只有当协议域名端口都一致才叫同源。

同源策略 :为了保护用户的隐私和数据安全,浏览器就会通过实施同源策略 来限制不同源 之间的直接通信。但也有一些情况不受同源策略的限制,分别是:Img标签下的Link标签下的Script标签下的(至于为什么它们不受限,请往下看)。

本篇我们就来一步一步的深入剖析JSONP这种方法之所以能实现跨域的原理,以及代码实操。

JSONP实现跨域的原理?

现在我们知道了同源策略的存在,也明白了不同源之间的数据传输是受到限制的,但是神奇的一幕发生了,我居然能直接在页面上展示出一张不同源的图片!

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
     <img src="https://img2.baidu.com/it/u=446981511,947966320&fm=253&fmt=auto&app
     =120&f=JPEG?w=750&h=500" alt="">
     // 这是一张百度上随便找来的图片
</body>
</html>
js 复制代码
const http = require('http')

http.createServer(function(req, res) {
    res.end('hello world')

}).listen(3000) // 项目运行在 3000 端口

要知道,要想展示出百度的这张图片,就势必得从我们本机的端口发出请求,也就势必的会造成跨域。但正是因为img标签不受同源策略的限制,所以它才能够正常的展示出来。

其实最开始的同源策略被打造的极其严格,严格到任何资源只要是不同源的就无法请求到,给当时的第三方图片展示带来了极大的麻烦。但是在项目中加载第三方图片的需求应该属于合理的,于是同源策略在后面又修改了一下,把img标签 排除在外,也就是放入白名单了。link标签Script标签同理。

于是,利用img标签不受同源策略的限制的原理,我们用来实现JSPON的跨域。

JSPON跨域的实现

现在我们知道,直接发送ajax请求会被同源策略拦截,于是我们将ajax请求塞在src属性里面,再发送给后端,这样前端就能正常发送请求给后端,后端也能返回数据。

但是相比于正常发送 ajax 请求给后端,这种方式有一个很大的区别,那就是不能直接操作后端返回来的数据

所以我们现在就得在原有的基础上进行封装,使得我们能够操作后端返回的数据。

那么,我们第一步的思路 就是创建一个jsonp函数,每次调用这个函数就会向后端发送请求,随后就可以通过then对后端返回的响应体进行操作。

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script>
        function jsonp(url) {
            return new Promise((resolve, reject) => {
                const script = document.createElement('script'); // 创建一个script标签
                script.src = url; // 设置script标签的src属性
                document.body.appendChild(script); // 将script标签添加到页面中
            })
        }

        jsonp('http://localhost:3000').then(res => {
            console.log(res);
        })
    </script>
</body>
</html>

以上第11,12、13行的代码,先是创建一个script标签,再设置script标签的src属性,最后将script标签添加到页面中:

要想将浏览器请求到的数据为我们所用,关键在于我们接下来要向后端传递的这个我们命名为callback的参数。 我们又在以上的基础上做出修改,在设置script标签的src属性过程中将callback参数拼接上去,传递给后端。

html 复制代码
<body>
    <script>
        function jsonp(url, cb) {
            return new Promise((resolve, reject) => {
                const script = document.createElement('script');
                window[cb] = function (data) {

                }

                script.src = `${url}?cb=${cb}`; // 拼接callback参数
                document.body.appendChild(script);
            })
        }
        jsonp('http://localhost:3000', 'callback').then(res => {  //传递callback参数给后端
            console.log(res)

        })
    </script>
</body>

注意修改后的第6行,我们在全局(window)上定义了一个名为cb(callback)的一个函数体,至于它起到的作用,我们很快会讲到。

现在既然我们前端将callback参数传递给了后端,现在后端自然要接收这个参数,并且,别忘记这么做的最终目的是为了凭借这个参数,我们能将后端的数据能够返回给前端。

于是我们将后端代码修改成这样:

js 复制代码
const http = require('http')

http.createServer(function(req, res) {
    const query = new URL(req.url, `http://${req.headers.host}`).searchParams 
    // 拿到前端传递的路径和地址
    
    if(query.get('cb')) {
        const cb = query.get('cb')       // 'callback'
        const data = 'hellow world'      // 我们想向前端返回的数据
        const result = `${cb}("${data}")`  // 拼接数据:'callback('hellow world')'

        res.end(result)                  // 返回给前端
    }

    // res.end('hello world')

}).listen(3000) // 项目运行在 3000 端口

现在重点来了:

在第4行,我们通过定义query来获取前端传递的路径与地址,因此在第8行我们就能通过query.get('cb')获取到前端地址传递过来的参数callback,假设我们在第9行定义我们想要向前端返回hellow world,只需要使用${cb}(${data})将我们想要向前端返回的信息拼接上去即可!

好了,这时候后端返回callback的调用 ,我们提前在全局定义的函数体就能起作用了,现在后端返回的hellow world就是实参,window[cb] = function (data)中的data就是形参。

那么现在我们只需添加上resolve(data),14行的.then中就能得到我们想要的数据!

js 复制代码
<body>
    <script>
        function jsonp(url, cb) {
            return new Promise((resolve, reject) => {
                const script = document.createElement('script'); // 创建一个script标签
                window[cb] = function (data) { // 全局定义function callback(){}
                  resolve(data);
                }

                script.src = `${url}?cb=${cb}`; // 设置script标签的src属性
                document.body.appendChild(script); // 将script标签添加到页面中
            })
        }
        jsonp('http://localhost:3000', 'callback').then(res => {
            console.log(res)    // 输出后端返回的数据

        })
    </script>
</body>

随着"hollow world"在控制台出现的那一刻,艺术已成!实现跨域的整个过程可谓是精妙绝伦!

最后

JSONP跨域原理: 利用img标签不受同源策略的限制,于是我们就可以通过将ajax请求塞在src属性里面,再发送给后端。

实现跨域的代码过程

  1. 借助script标签的src属性不受同源策略的影响,来发送请求。
  2. 给后端携带一个参数 callback, 并在前端定义 callback 函数体。
  3. 后端返回 callback 的调用形式并将要响应的值作为 callback 的实参。
  4. 当浏览器接收到响应后, 就会触发全局的 callback 函数从而让 callback 以参数的形式 接受到后端的响应。

缺陷:需要后端配合, 只能发get请求。

用白话来说,JSONP这种方法实现跨域的代码过程就是:前端先给后端一个参数,后端再把这个参数写成调用的样子,然后再把后端想要返回给前端的数据作为实参塞到这个调用体里来,最后整体返回给前端。

相关推荐
崔庆才丨静觅6 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60616 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了7 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅7 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅7 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅7 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment7 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅8 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊8 小时前
jwt介绍
前端