使用JSONP来取代AJAX进行跨域
说到用JavaScript动态加载内容,一般都会想到AJAX。
但AJAX所用的XML其实并非必须,还有其他类型的数据结构(例如JSON和HTML)也可以实现。
而且AJAX有个很大的问题:由于JavaScript的安全限制,AJAX的跨域非常麻烦。
但如果使用JSONP的话,问题就很容易搞定了。
JSONP就是JSON with Padding的缩写,意思是JSON加上一些填充。
那么究竟填充什么呢?这就要说下原理了。
当使用script标签时,浏览器可以加载来自外域的JavaScript文件。而JSON本身就是一个JavaScript的对象,所以浏览器也可以直接加载它们。
但单纯的加载毫无用处,因为JSON只是数据,我们还需要处理它。因此,假如用括号将JSON数据包围起来,然后传给一个JavaScript function,那这个function就能处理这个JSON数据了。
因此从技术上来说,JSONP并不是数据,而是JavaScript代码,和JSON是2个稍有区别的东西。
首先来看最简单的实现:
客户端:
<html> <head> <title>JSONP Test</title> <script> function test(text) {alert(text.hello);} </script> <script src="test.json"></script> </head> <body> </body> </html>
服务器端(就是那个test.json)
test({"hello": "hello world!"})
在浏览器中打开这个页面,将会弹出一个对话框,内容是hello world!。
事实上,我们的test.json文件就是个JavaScript文件,它调用test方法,并将其中的JSON数据({"hello": "hello world!"})作为参数。
也就是说,JSONP就是一次函数调用而已,明白这点就好办了。
不过动态加载当然不可能直接用script标签来做,但JavaScript是可以操作DOM的,所以我们只需要把script标签添加到DOM,就能实现动态加载了。
当然,改写DOM是个很麻烦的过程,我们可以使用一些JavaScript库来实现,下面就给个jQuery和GAE的例子:
客户端:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta name="author" content="keakon" /> <title>JSONP Test</title> <script type="text/javascript" src="http://www.google.com/jsapi"></script> <script type="text/javascript"> google.load("jquery", "1.3.1"); </script> </head> <body> <script> $.getJSON("http://localhost:8080/?jsoncallback=?", function (json) { alert(json.text); } ); </script> </body> </html>
服务器端(本来是发在Google的论坛的,所以注释用的是英文,将就看吧):
import wsgiref.handlers
from google.appengine.ext import webapp
class MainHandler(webapp.RequestHandler):
def get(self):
# Since browsers can't handle application/json as a text,
# and actually, it's a JavaScript code, not JSON, while I request jsoncallback,
# I use text/javascript to both display and process it.
self.response.headers['Content-Type'] = 'text/javascript; charset=UTF-8'
json = {"text": "Hello world!"} # the json data I want to output
jsoncallback = self.request.get('jsoncallback') # the jsoncallback argument
# if doesn't contain jsoncallback argument, simply output it
# otherwise, it should output jsoncallback_argument(json data)
# eg: hello({"text": "Hello world!"})
output = '%s(%s)' % (jsoncallback, json) if jsoncallback else json
self.response.out.write(output)
def main():
application = webapp.WSGIApplication([('/', MainHandler)])
wsgiref.handlers.CGIHandler().run(application)
if __name__ == '__main__':
main()
jQuery隐藏了很多实现,实际上它会自动判断jsoncallback=?,然后将?替换为一个随机生成的函数名(实际上就是我们的匿名函数),于是就形成了一次函数调用。
注意jsoncallback不是必须的,只是jQuery需要它的名字类似callback。
而且大多数都遵循这个命名规则,所以JSONP也被叫作JSON with callbacks。
个人也比较喜欢后者,因为更加贴近其原理,而不是其表面现象(填充了函数名和括号的JSON)。
当然,你可以更改这个函数名,但必须使用$.AJAX函数的jsonp参数来指定,$.getJSON函数是不提供这个参数的。
还在为AJAX跨域苦恼的不妨试试JSONP吧,而且JSON本身比XML也方便很多。
文章来自网络,个人收藏,如有冒犯敬请原谅!