Chrome扩展及应用开发 入门笔记(二)跨域请求

manifest.json:

文件可以告诉Chrome关于这个扩展的相关信息,它是整个扩展的入口,也是Chrome扩展必不可少的部分


注:

Google的官方文档中对于扩展和应用给出了两个不同的Manifest介绍界面,这是因为有些属性只能由扩展使用,而有些属性只能由应用使用。

如果这两者同时出现在同一个Manifest文件中,就会使Chrome困惑,不知是按照扩展对待这个程序还是按照应用来对待这个程序。

但无论是扩展还是应用,它们的Manifest又有很多共有的属性。


manifest.json模板:

Chrome扩展的Manifest必须包含nameversionmanifest_version属性,目前来说manifest_version属性值只能为数字2,对于应用来说,还必须包含app属性

其他常用的可选属性还有 browser_actionpage_actionbackgroundpermissionsoptions_pagecontent_scripts

所以我们可以保留一份manifest.json模板,当编写新的扩展时直接填入相应的属性值。

可以应对大部分的扩展的模板:


{
    "app": {
        "background": {
            "scripts": ["background.js"]
        }
    },
    "manifest_version": 2,
    "name": "My Extension",
    "version": "versionString",
    "default_locale": "en",
    "description": "A plain text description",
    "icons": {
        "16": "images/icon16.png",
        "48": "images/icon48.png",
        "128": "images/icon128.png"
    },
    "browser_action": {
        "default_icon": {
            "19": "images/icon19.png",
            "38": "images/icon38.png"
        },
        "default_title": "Extension Title",
        "default_popup": "popup.html"
    },
    "page_action": {
        "default_icon": {
            "19": "images/icon19.png",
            "38": "images/icon38.png"
        },
        "default_title": "Extension Title",
        "default_popup": "popup.html"
    },
    "background": {
        "scripts": ["background.js"]
    },
    "content_scripts": [
        {
            "matches": ["http://www.google.com/*"],
            "css": ["mystyles.css"],
            "js": ["jquery.js", "myscript.js"]
        }
    ],
    "options_page": "options.html",
    "permissions": [
        "*://www.google.com/*"
    ],
    "web_accessible_resources": [
        "images/*.png"
    ]
}
在官方文档中可以找到完整的Manifest属性列表,扩展在 https://developer.chrome.com/extensions/manifest

操作用户正在浏览的页面:

实际上就是对用户当前浏览页面的DOM进行操作。通过Manifest中的content_scripts属性可以指定将哪些脚本何时注入到哪些页面中,

当用户访问这些页面后,相应脚本即可自动运行,从而对页面DOM进行操作。

Manifest的content_scripts属性值为数组类型,数组的每个元素可以包含:

matches //定义了哪些页面会被注入脚本

exclude_matches//定义了哪些页面不会被注入脚本

css /js //对应要注入的样式表和JavaScript

run_at//何时进行注入

all_frames//脚本是否会注入到嵌入式框架中

include_globs//

exclude_globs//全局URL匹配,最终脚本是否会被注入由matchesexclude_matchesinclude_globsexclude_globs的值共同决定。

简单的说,如果URL匹配mathces值的同时也匹配include_globs的值,会被注入;如果URL匹配exclude_matches的值或者匹配exclude_globs的值,则不会被注入。


content_scripts中的脚本只是共享页面的DOM1,而并不共享页面内嵌JavaScript的命名空间。也就是说,如果当前页面中的JavaScript有一个全局变量acontent_scripts中注入的脚本也可以有一个全局变量a,两者不会相互干扰。

当然你也无法通过content_scripts访问到页面本身内嵌JavaScript的变量和函数。

跨域请求:


跨域指的是JavaScript通过XMLHttpRequest请求数据时,调用JavaScript的页面所在的域和被请求页面的域不一致。

对于网站来说,浏览器出于安全考虑是不允许跨域。另外,对于域相同,但端口或协议不同时,浏览器也是禁止的。下表给出了进一步的说明:


URL 说明 是否允许请求
http://a.example.com/
http://a.example.com/a.txt
同域下 允许
http://a.example.com/
http://a.example.com/b/a.txt
同域下不同目录 允许
http://a.example.com/
http://a.example.com:8080/a.txt
同域下不同端口 不允许
http://a.example.com/
https://a.example.com/a.txt
同域下不同协议 不允许
http://a.example.com/
http://b.example.com/a.txt
不同域下 不允许
http://a.example.com/
http://a.foo.com/a.txt
不同域下 不允许


解决:

但这个规则如果同样限制Chrome扩展应用,就会使其能力大打折扣,所以Google允许Chrome扩展应用不必受限于跨域限制。

但出于安全考虑,需要在Manifest的permissions属性中声明需要跨域的权限。


比如,如果我们想设计一款获取维基百科数据并显示在其他网页中的扩展,就要在Manifest中进行如下声明:

{
    ...
    "permissions": [
        "*://*.wikipedia.org/*"
    ]
}

这样Chrome就会允许你的扩展在任意页面请求维基百科上的内容了。


可以利用如下的代码发起异步请求:

function httpRequest(url, callback){
    var xhr = new XMLHttpRequest();
    xhr.open("GET", url, true);
    xhr.onreadystatechange = function() {
        if (xhr.readyState == 4) {
            callback(xhr.responseText);
        }
    }
    xhr.send();
}

这样每次发起请求时,只要调用httpRequest函数,并传入要请求的URL和接收返回结果的函数就可以了。为什么要使用callback函数接收请求结果,而不直接用return将结果作为函数值返回呢?因为XMLHttpRequest不会阻塞下面代码的运行。

为了更加明确地说清上述问题,让我们来举两个例子。

function count(n){
    var sum = 0;
    for(var i=1; i<=n; i++){
        sum += i;
    }
    return sum;
}

var c = count(5)+1;
console.log(c);

这个例子会在控制台显示16,
因为:<code>count</code>函数是一个阻塞函数,在它没有运行完之前它会阻塞下面的代码运行,
所以直到<code>count</code>计算结束后才将结果返回后再加<code>1</code>赋给变量<code>c</code>,最后将变量<code>c</code>的值打印出来


unction httpRequest(url){
    var xhr = new XMLHttpRequest();
    xhr.open("GET", url, true);
    xhr.onreadystatechange = function() {
        if (xhr.readyState == 4) {
            return xhr.responseText;
        }
    }
    xhr.send();
}

var html = httpRequest('test.txt');
console.log(html);

虽然请求的资源内容为<code>Hello World!</code>,但却并没有正确地显示出来

原因:
<code>httpRequest</code>函数不是一个阻塞函数,在它没运行完之前后面的代码就已经开始运行,
这样<code>html</code>变量在<code>httpRequest</code>函数没返回值之前就被赋值,所以最终的结果必然就是<code>undefined</code>了。



既然这样,如何将非阻塞函数的最终结果传递下去呢?方法就是使用回调函数。在Chrome扩展应用的API中,大部分函数都是非阻塞函数,

所以使用回调函数传递结果的方法以后会经常用到。

让我们来用回调函数的形式重写第二个例子:

function httpRequest(url, callback){
    var xhr = new XMLHttpRequest();
    xhr.open("GET", url, true);
    xhr.onreadystatechange = function() {
        if (xhr.readyState == 4) {
            callback(xhr.responseText);
        }
    }
    xhr.send();
}

var html;
httpRequest('test.txt', function(result){
    html = result;
    console.log(html);  <code>httpRequest</code>函数运行的结果已经被正确地打印出来了。
});

实战编写一款显示用户IP的扩展:

{
    "manifest_version": 2,
    "name": "查看我的IP",
    "version": "1.0",
    "description": "查看我的电脑当前的公网IP",
    "icons": {
        "16": "images/icon16.png",
        "48": "images/icon48.png",
        "128": "images/icon128.png"
    },
    "browser_action": {
        "default_icon": {
            "19": "images/icon19.png",
            "38": "images/icon38.png"
        },
        "default_title": "查看我的IP",
        "default_popup": "popup.html"
    },
    "permissions": [
        "http://sneezryworks.sinaapp.com/ip.php" //的Manifest定义了这个扩展允许对http://sneezryworks.sinaapp.com/ip.php发起跨域请求
    ]
}

popup.html:

<html>
<head>
<style>
* {
    margin: 0;
    padding: 0;
}

body {
    width: 400px;
    height: 100px;
}

div {
    line-height: 100px;
    font-size: 42px;
    text-align: center;
}
</style>
</head>
<body>
<div id="ip_div">正在查询……</div>
<script src="js/my_ip.js"></script>//js文件夹下有my_ip.js文件
</body>
</html>

my_ip.js:注 此 js获取 IP实践后发现不行

function httpRequest(url, callback){
    var xhr = new XMLHttpRequest();
    xhr.open("GET", url, true);
    xhr.onreadystatechange = function() {
        if (xhr.readyState == 4) {
            callback(xhr.responseText);
        }
    }
    xhr.send();
}

httpRequest('http://sneezryworks.sinaapp.com/ip.php', function(ip){
    document.getElementById('ip_div').innerText = ip;
});


结果:

Chrome扩展及应用开发 入门笔记(二)跨域请求_第1张图片


源码地址

https://github.com/xiaohuhuanxiang/chromedev/tree/chrome/getIP

安全问题:


作为一个开发者,安全问题永远都不应被轻视。在你从外域获取到数据后,不要轻易作为当前页面元素的innerHTML直接插入,更不要用eval函数去执行它,否则很可能将用户置于危险的境地。如果要将请求到的数据写入页面,可以使用innerText,就像我们这个查看IP的扩展那样。如果是JSON格式是数据就使用JSON.parse函数去解析。为了避免请求数据返回的格式错误,结合try-catch一起使用也是不错的选择。



你可能感兴趣的:(Chrome扩展及应用开发 入门笔记(二)跨域请求)