阅读 543

[干货]前端小众-谷歌插件跨域

  这几天谷歌更新到了73版本后,有人提醒我之前写的谷歌翻译插件报错,看了下是跨域下cors报红提醒,经过我多次尝试解决,最终得以解决,此篇仅以我的解决措施,以及一些尝试来讲述这一过程。

谷歌插件编写传送门

首先介绍下吧,谷歌插件多数时候,是对谷歌浏览器功能的一些拓展,甚至直接依赖本身实现一些桌面端应用等,常见的有日历,页面刷新,邮箱等,强大一点的有远程桌面控制等,当我们科学上网的时候,大家可以去找找,有点废话

开发文档地址传送门

跨域问题

谷歌插件是一个沙箱系统。

  借官方图一样用,background我称之为后台,contentscript为内容体。

  之前整个插件作用机制如图:

  在之前版本中,通过官方给出的配置即可解决跨域,在content中请求数据,并操作浏览器页面的dom,配置如下,这可能是多数开发者之前的配置,也是查阅很多资料,大家给出的解决方法,然而此次我是失效了,因为之前已配置好。

  这是流行的配置方式,你需要请求那个api的接口,就写上地址即可,之后谷歌插件的机制,会正常以json的形式请求。 更新版本后,控制台爆红:

Access to fetch at 'another-site.com/' from origin 'example.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

解决措施

暴力解决法,此法堪称杀猪刀,直接关了浏览器的安全机制

Chrome49之后的版本:Windows:  
1.关闭所有的chrome浏览器。 
2.新建一个chrome快捷方式,右键“属性”,“快捷方式”选项卡里选择“目标”,添加  --args --disable-web-security --user-data-dir 
3.通过快捷方式打开谷歌浏览器 
 
MAC: 
 
打开终端,确保chrome 已经完全退出。 
open -a /Applications/Google\ Chrome.app --args --disable-web-security --user-data-dir
复制代码

之后浏览器会提示

上述方法,临时过渡了几天。

  之后我想到的是jsonp解决方式,因为我所需要的资源是提供了JSONP的请求方式,于是我自信的将请求方式改为了jsonp,模拟代码如下:

<!--start.js-->
function jsonpFunc(jsonpParams){
    console.log(jsonpParams)
}
$.ajax({
    url: 'http://example.com',
    data: 
    {
        apiKey:apikey,
        sign:sign,
        to:'zh',
        query:'Hello world'
    },
    dataType: "jsonp",
    jsonp:'jsonpParams',
    jsonpCallback:'jsonpFunc',
    //jsonpCallback:jsonpFunc, // Uncaught ReferenceError: jQuery17105683612572029233_1323808231542 is not defined
    success: function(msg){
        newNode.src = msg.data;
    },
    error: function(msg){
        console.log(msg.data);
    }
})
复制代码

  在这里我遇到两个问题,一个是回调函数加引号,该函数回去找浏览器中j上jonpFunc函数,在当前文件内,我定义的函数,压根就不在对方的考虑范围内,我试过在background.js中定义,window下挂载,由于沙箱的规则,无一生效。 如果直接调用函数,不加引号,则浏览器控制台会报错上方注释信息,并且点击定位能看到接口返回的信息,但是遗憾的是我无法取得,并对它进行处理。

在解决过程中找到一个很好的帖子【干货】Chrome插件(扩展)开发全攻略,在这两张图中我找到了思路

可操作
通信方案
这就是解决方案,并且可以避免跨域请求的问题~
互传
只要保持双方间的通信即可,注意其中有个限制是,只能存在一个队列,无论定义几个这种队列

队列
当只要有一个先到达B,则另外一个取消通信,开始下轮。针对此情况我不能单独写几个队列,只能通过一组这种双方通信的方式,里面做情况拆分

<!--background.js 不可操作浏览器页面,可跨域-->
// 监听函数 API,获取从content_script传递过来的内容
chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {

    var query = '',_promise=null;
    if(request.currentRequstName == 'yandex'){
        query = request.subtitle;
        var apiKey = request.apiKey;
        var to = request.configInfo.aimLang == 'undefined' ? 'zh' : request.configInfo.aimLang;
        if (query == '') return

        _promise = ajaxThen("https://translate.yandex.net/api/v1.5/tr.json/translate", {
            key: apiKey,
            text: query,
            lang: to
        })
    }else if(request.currentRequstName == "baidu"){ // 分类请求
        // ...
    }else if(request.currentRequstName == "youdao"){
        // ...
    }

});

function ajaxThen(url, params) { // 请求封装
    var dtd = $.Deferred();
    $.ajax({
        url: url,
        type: 'post',
        data: params,
        dataType: 'json'
    }).then(function (data) {
        dtd.resolve(data);
    }, function () {
        toastr.error("submit failure", "oprate failure");
        dtd.reject();
    });
    return dtd.promise();
}



<!--start.js 可操作浏览器页面,不可跨域-->

 // 发送数据到background,让它取请求数据
    chrome.runtime.sendMessage({
        currentRequstName: 'youdao', // "baidu","yandex"
        apiKey: apiKey,
        subtitle: query,
        configInfo: configInfo,
        sign: sign,
        from: from,
        salt: salt
    }, function (response) {
        // when background get info
    });


// 接收得到background.js页面得到的数据,之后就是操作了
var beforeGet = null;
// 监听函数,获取冲backgroud传递过来的数据
chrome.extension.onMessage.addListener(
    function (request, sender, sendResponse) {
        if (beforeGet != request) { // 避免重复操作
            beforeGet = request; 
            console.log("%c%s","color: red; background: yellow; font-size: 24px;",JSON.stringify(request[0]));
            // 根据不同请求方式,来选择操作模式
            if (request[2] == 'yandex') {
                yandexSendOkThenChangeSubtitle(request)
            } else if (request[2] == "baidu") {
                baiduSendThenChangeSubtitle(request)
            } else if (request[2] == "youdao") {
                youdaoSendThenChangeSubtitle(request)
            }
        }
    }
);
复制代码

以上就是核心实现,之前的逻辑复用即可。

  此次修复完毕,希望撑一段时间,此项目开源 chrome-extension-udemy-translate 维持八个多月,体会到了当中的苦与乐,希望能真正帮到一些朋友,开源不易(一直开一直爽?

See Ya

参考文献资料和帖子

chrome developer

Chrome插件(扩展)开发全攻略

StackOverflow: chrome extension jsonp

MDN fetch

历史文章传送门

👨‍🏫图片压缩Canvas

Vue嵌入iframe,iframe如何跨域调用vue内路由

记vue下悬浮贴合顶部实现

一加5t ,安卓p系统降级

编写一个谷歌插件翻译Udemy+NetFlix字幕(相关)

关注下面的标签,发现更多相似文章
评论