2006-12-5
我在最近的一个
web
项目中为了实现
bookmark
功能碰到了
javascript
跨域访问的问题。起初,在
google
上搜的很多解决方案并不适用于我的情形,只在有一篇文章中提到的远程加载
javascript
方法从理论上看到了解决的希望。但可惜作者只是一笔带过,并未用例子详细说明,所以不得不摸索了一阵才把这个问题搞定。在此,希望通过本文为同样被这个问题困扰的朋友们提供一个解决方法作为参考。如有错误,欢迎指正!
Bookmark
是目前许多
web2.0
网站
(http://del.icio.us/, www.diigo.com
等)都提供的热门
feature
。它能将互联网上自己喜欢的网页收藏到
Bookmark
服务器上。本文要解决的问题就发生在用户提交网页
URL
(还包括
Tag, Notes
等)给
Bookmark
服务器时。
关于
URL
的提交至少可以有三种方式:
1.
登陆
Bookmark
服务器的提交页面,将要收藏的
URL
通过该页面提交给服务器。
2.
安装浏览器插件,通过插件将
URL
提交给服务器。
3.
从
Bookmark
服务器动态加载
javascript
小工具到当前页面,通过它来完成提交工作。(参考
diigo
的例子,收藏一个网页链接到浏览器收藏夹,链接的
URL
是一段
javascript
代码,它会从服务器加载一段
javascript
注入当前网页)
第一种方式开发起来最简单,但对用户来讲比较麻烦,每次都需要先登陆
Bookmark
服务器才能完成提交;第二种方式我并不熟悉插件开发,而且用户也不喜欢太多的插件堆满自己的浏览器;第三种方式开发难度小,又避免了每次登陆服务器的麻烦,所以我最终采用了它。
上面讲的第三种方式中动态加载的
javascript
小工具除了需要生成
UI
供用户填写信息(
URL
,
tag
,
notes
等),当用户点击提交的时候,还要完成与服务器通信的功能。在没有跨域访问经验的情况下,最先想到的当然是
ajax
!但很快就会发现根本行不通。
跨域访问,简单来说就是
A
网站的
javascript
代码试图访问
B
网站,包括提交内容和获取内容。由于安全原因,跨域访问是被各大浏览器所默认禁止的。写过跨域访问
ajax
的朋友相信都遇到过被告知“没有权限”的情况。通过
XMLHttp
来发送数据给
Bookmark
服务器的尝试失败了。于是,看到网上的一些资料,我又开始尝试用
javascript
小工具在用户网页动态创建一个隐藏的
iframe, iframe
的
src
指向服务器的一个
servlet
,试图通过调用
iframe
中提供的
javascript
来完成与服务器的通信。但不幸的是,用户网页中的
javascript
代码访问
iframe
也被浏览器归为跨域访问(特指
iframe
的
src
指向其它网站的情形),尝试再次失败。
最终,在一篇文章中看到,与
iframe
不同,如果
A
网站从
B
网站加载
javascript
,
A
网站可以自由的访问该
javascript
的内容,并不会被浏览器认为是跨域访问。模仿刚才
iframe
的思路,当用户点击提交时,可以动态创建一个
javascript
对象,该对象的
src
指向
Bookmark
服务器的一个
servlet
,注意:
URL
、
Tag
、
Notes
、
User
、
Password
等信息被作为
src URL
参数传给服务器。请看下面的代码:
var
url =
"http://localhost:8080/Deeryard/BookmarkServlet?"
+
"url="
+ url_source +
"&"
+
"title="
+ title +
"&"
+
"tag="
+ tag +
"&"
+
"notes="
+ notes +
"&"
+
"user="
+ user
+ "&"
+
"password="
+ password;
url = encodeURI(url);
//Submit to server with a trick
var
js_obj = document.createElement(
"script"
);
js_obj.type =
"text/javascript"
;
js_obj.setAttribute(
"src"
, url);
//Get response from server by appending it to document
document.body.appendChild(js_obj);
上面例子中,
js_obj.setArrribute()
将信息作为
src
的
URL
参数提交给了
Bookmark servlet
。那么用户又如何取得服务器的响应信息呢?答案就是最末一行代码,
servlet
的输出必须是
javascript
代码,它可以调用用户网页上的其他
javascript
函数,以及操作
dom
对象。下面的
servlet
代码生成了一个
javascript
函数调用:
out.write("onServerResponse(INADEQUATE_INFORMATION);");
document.body.appendChild(js_obj)
执行后
onServerResponse(
INADEQUATE_INFORMATION)
就会得到执行,使客户网页响应服务器结果。这样一个完整的通信过程就完成了。
来总结一下这个案例,首先与很多跨域访问的情形不同,本文提到的跨域访问需要对服务器端进行控制,即让服务器端
Servlet
来适应客户端网页
javascript
的需求;而其他一些常见的例子则对服务器端没有控制能力,比如从其他网站抓内容的小偷程序。另外,需要注意的是这种方法中实际用到了
get
方法来提交信息,从一些资料上看到,
get
方法每次提交的信息不能超过
2k
。