Chrome扩展及应用开发 入门笔记(二)跨域请求
2015-07-01 19:06
519 查看
manifest.json:
文件可以告诉Chrome关于这个扩展的相关信息,它是整个扩展的入口,也是Chrome扩展必不可少的部分注:
Google的官方文档中对于扩展和应用给出了两个不同的Manifest介绍界面,这是因为有些属性只能由扩展使用,而有些属性只能由应用使用。
如果这两者同时出现在同一个Manifest文件中,就会使Chrome困惑,不知是按照扩展对待这个程序还是按照应用来对待这个程序。
但无论是扩展还是应用,它们的Manifest又有很多共有的属性。
manifest.json模板:
Chrome扩展的Manifest必须包含name、
version和
manifest_version属性,目前来说
manifest_version属性值只能为数字2,对于应用来说,还必须包含
app属性。
其他常用的可选属性还有
browser_action、
page_action、
background、
permissions、
options_page、
content_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匹配,最终脚本是否会被注入由
matches、
exclude_matches、
include_globs和
exclude_globs的值共同决定。
简单的说,如果URL匹配
mathces值的同时也匹配
include_globs的值,会被注入;如果URL匹配
exclude_matches的值或者匹配
exclude_globs的值,则不会被注入。
content_scripts中的脚本只是共享页面的DOM1,而并不共享页面内嵌JavaScript的命名空间。也就是说,如果当前页面中的JavaScript有一个全局变量
a,
content_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;
});
结果:
源码地址
https://github.com/xiaohuhuanxiang/chromedev/tree/chrome/getIP安全问题:
作为一个开发者,安全问题永远都不应被轻视。在你从外域获取到数据后,不要轻易作为当前页面元素的
innerHTML直接插入,更不要用
eval函数去执行它,否则很可能将用户置于危险的境地。如果要将请求到的数据写入页面,可以使用
innerText,就像我们这个查看IP的扩展那样。如果是JSON格式是数据就使用
JSON.parse函数去解析。为了避免请求数据返回的格式错误,结合
try-catch一起使用也是不错的选择。
相关文章推荐
- VC修改窗口属性 GetWindowLong(), SetWindowLong()
- 在C#中运行PowerShell
- 设置tomcat自启动时段
- iOS 钥匙串 指纹识别 get和Post请求的区别
- android 多线程 图片:加载
- 如何优化Eclipse
- java虚拟机
- C#学习笔记(四):委托和事件
- plsql连接到oracle11g
- mysql5.6.24更改character-set-server的字符集
- jeecg t:upload 使用后台取参数问题
- 常用的方法封装成Jar包并调用
- MyBatis架构设计及源代码分析系列(一):MyBatis架构
- 在ruby 2 和rails 4 里面使用send_file 下载文件的时候。在IE下面中文名出现乱码的处理方式
- 纯html 图片定高定宽,裁剪无需后台代码
- yii2-datepicker/datetimepicker插件使用
- 二、当应用接收到内存警告时应该怎么处理
- 前端规范
- 最初,世上有许多令狐冲
- bzoj1017 [JSOI2008]魔兽地图DotR