在已經(jīng)建立了前端表單之后,就要實(shí)現(xiàn)前端和后端之間的數(shù)據(jù)傳遞。在工程中,常用到一個(gè)被稱之為 ajax() 的方法。
關(guān)于 ajax 的故事,需要濃墨重彩,因?yàn)樗銐蚓省?/p>
ajax 是“Asynchronous Javascript and XML”(異步 JavaScript 和 XML)的縮寫,在它的發(fā)展歷程中,匯集了眾家貢獻(xiàn)。比如微軟的 IE 團(tuán)隊(duì)曾經(jīng)將 XHR(XML HttpRequest) 用于 web 瀏覽器和 web 服務(wù)器間傳輸數(shù)據(jù),并且被 W3C 標(biāo)準(zhǔn)采用。當(dāng)然,也有其它公司為 Ajax 技術(shù)做出了貢獻(xiàn),雖然它們都被遺忘了,比如 Oddpost,后來(lái)被 Yahoo!收購(gòu)并成為 Yahoo! Mail 的基礎(chǔ)。但是,真正讓 Ajax 大放異彩的 google 是不能被忽視的,正是 google 在 Gmail、Suggest 和 Maps 上大規(guī)模使用了 Ajax,才使得人們看到了它的魅力,程序員由此而興奮。
技術(shù)總是在不斷進(jìn)化的,進(jìn)化的方向就是用著越來(lái)越方便。
回到上一節(jié)使用的 jQuery,里面就有 ajax() 方法,能夠讓程序員方便的調(diào)用。
ajax() 方法通過(guò) HTTP 請(qǐng)求加載遠(yuǎn)程數(shù)據(jù)。
該方法是 jQuery 底層 AJAX 實(shí)現(xiàn)。簡(jiǎn)單易用的高層實(shí)現(xiàn)見(jiàn) $.get, $.post 等。$.ajax() 返回其創(chuàng)建的 XMLHttpRequest 對(duì)象。大多數(shù)情況下你無(wú)需直接操作該函數(shù),除非你需要操作不常用的選項(xiàng),以獲得更多的靈活性。
最簡(jiǎn)單的情況下,$.ajax() 可以不帶任何參數(shù)直接使用。
在上文介紹 Ajax 的時(shí)候,用到了一個(gè)重要的術(shù)語(yǔ)——“異步”,與之相對(duì)應(yīng)的叫做“同步”。我引用來(lái)自阮一峰的網(wǎng)絡(luò)日志中的通俗描述:
"同步模式"就是上一段的模式,后一個(gè)任務(wù)等待前一個(gè)任務(wù)結(jié)束,然后再執(zhí)行,程序的執(zhí)行順序與任務(wù)的排列順序是一致的、同步的;"異步模式"則完全不同,每一個(gè)任務(wù)有一個(gè)或多個(gè)回調(diào)函數(shù)(callback),前一個(gè)任務(wù)結(jié)束后,不是執(zhí)行后一個(gè)任務(wù),而是執(zhí)行回調(diào)函數(shù),后一個(gè)任務(wù)則是不等前一個(gè)任務(wù)結(jié)束就執(zhí)行,所以程序的執(zhí)行順序與任務(wù)的排列順序是不一致的、異步的。
"異步模式"非常重要。在瀏覽器端,耗時(shí)很長(zhǎng)的操作都應(yīng)該異步執(zhí)行,避免瀏覽器失去響應(yīng),最好的例子就是 Ajax 操作。在服務(wù)器端,"異步模式"甚至是唯一的模式,因?yàn)閳?zhí)行環(huán)境是單線程的,如果允許同步執(zhí)行所有 http 請(qǐng)求,服務(wù)器性能會(huì)急劇下降,很快就會(huì)失去響應(yīng)。
看來(lái),ajax() 是前后端進(jìn)行數(shù)據(jù)傳輸?shù)闹匾巧?/p>
承接上一節(jié)的內(nèi)容,要是用 ajax() 方法,需要修改 script.js 文件內(nèi)容即可:
$(document).ready(function(){
$("#login").click(function(){
var user = $("#username").val();
var pwd = $("#password").val();
var pd = {"username":user, "password":pwd};
$.ajax({
type:"post",
url:"/",
data:pd,
cache:false,
success:function(data){
alert(data);
},
error:function(){
alert("error!");
},
});
});
});
在這段代碼中,var pd = {"username":user, "password":pwd};
意即將得到的 user 和 pwd 值,放到一個(gè) json 對(duì)象中(關(guān)于 json,請(qǐng)閱讀《標(biāo)準(zhǔn)庫(kù)(8)》),形成了一個(gè) json 對(duì)象。接下來(lái)就是利用 ajax() 方法將這個(gè) json 對(duì)象傳給后端。
jQuery 中的 ajax() 方法使用比較簡(jiǎn)單,正如上面代碼所示,只需要 $.ajax()
即可,不過(guò)需要對(duì)立面的參數(shù)進(jìn)行說(shuō)明。
前端通過(guò) ajax 技術(shù),將數(shù)據(jù)已 json 格式傳給了后端,并且指明了對(duì)象目錄"/"
,這個(gè)目錄在 url.py 文件中已經(jīng)做了配置,是由 handlers 目錄的 index.py 文件的 IndexHandler 類來(lái)出來(lái)。因?yàn)槭怯?post 方法傳的數(shù)據(jù),那么在這個(gè)類中就要有 post 方法來(lái)接收數(shù)據(jù)。所以,要在 IndexHandler 類中增加 post(),增加之后的完善代碼是:
#!/usr/bin/env Python
# coding=utf-8
import tornado.web
class IndexHandler(tornado.web.RequestHandler):
def get(self):
self.render("index.html")
def post(self):
username = self.get_argument("username")
password = self.get_argument("password")
self.write(username)
在 post() 方法中,使用 get_argument() 函數(shù)來(lái)接收前端傳過(guò)來(lái)的數(shù)據(jù),這個(gè)函數(shù)的完整格式是 get_argument(name, default=[], strip=True)
,它能夠獲取 name 的值。在上面的代碼中,name 就是從前端傳到后端的那個(gè) json 對(duì)象的鍵的名字,是哪個(gè)鍵就獲取該鍵的值。如果獲取不到 name 的值,就返回 default 的值,但是這個(gè)值默認(rèn)是沒(méi)有的,如果真的沒(méi)有就會(huì)拋出 HTTP 400。特別注意,在 get 的時(shí)候,通過(guò) get_argument() 函數(shù)獲得 url 的參數(shù),如果是多個(gè)參數(shù),就獲取最后一個(gè)的值。要想獲取多個(gè)值,可以使用 get_arguments(name, strip=true)
。
上例中分別用 get_argument() 方法得到了 username 和 password,并且它們都是 unicode 編碼的數(shù)據(jù)。
tornado.web.RequestHandler 的方法 write(),即上例中的 self.write(username)
,是后端向前端返回?cái)?shù)據(jù)。這里返回的實(shí)際上是一個(gè)字符串,也可返回 json 字符串。
如果讀者要查看修改代碼之后的網(wǎng)站效果,最有效的方式先停止網(wǎng)站(ctrl+c),在從新執(zhí)行 Python server.py
運(yùn)行網(wǎng)站,然后刷新瀏覽器即可。這是一種較為笨拙的方法。一種靈巧的方法是開(kāi)啟調(diào)試模式。是否還記得?在設(shè)置 setting 的時(shí)候,寫上 debug = True
就表示是調(diào)試模式了(參閱:用 tornado 做網(wǎng)站 (1))。但是,調(diào)試模式也不是十全十美,如果修改模板,就不會(huì)加載,還需要重啟服務(wù)。反正重啟也不麻煩,無(wú)妨啦。
看看上面的代碼效果:
http://wiki.jikexueyuan.com/project/start-learning-python/images/30501.png" alt="" />
這是前端輸入了用戶名和密碼之后,點(diǎn)擊 login 按鈕,提交給后端,后端再向前端返回?cái)?shù)據(jù)之后的效果。就是我們想要的結(jié)果。
按照流程,用戶在前端輸入了用戶名和密碼,并通過(guò) ajax 提交到了后端,后端借助于 get_argument() 方法得到了所提交的數(shù)據(jù)(用戶名和密碼)。下面要做的事情就是驗(yàn)證這個(gè)用戶名和密碼是否合法,其體現(xiàn)在:
這個(gè)驗(yàn)證工作完成之后,才能允許用戶登錄,登錄之后才能繼續(xù)做某些事情。
首先,在 methods 目錄中(已經(jīng)有了一個(gè) db.py)創(chuàng)建一個(gè)文件,我命名為 readdb.py,專門用來(lái)存儲(chǔ)讀數(shù)據(jù)用的函數(shù)(這種劃分完全是為了明確和演示一些應(yīng)用方法,讀者也可以都寫到 db.py 中)。這個(gè)文件的代碼如下:
#!/usr/bin/env Python
# coding=utf-8
from db import *
def select_table(table, column, condition, value ):
sql = "select " + column + " from " + table + " where " + condition + "='" + value + "'"
cur.execute(sql)
lines = cur.fetchall()
return lines
上面這段代碼,建議讀者可以寫上注釋,以檢驗(yàn)自己是否能夠?qū)⒁酝闹R(shí)融會(huì)貫通地應(yīng)用。恕我不再解釋。
有了這段代碼之后,就進(jìn)一步改寫 index.py 中的 post() 方法。為了明了,將 index.py 的全部代碼呈現(xiàn)如下:
#!/usr/bin/env Python
# coding=utf-8
import tornado.web
import methods.readdb as mrd
class IndexHandler(tornado.web.RequestHandler):
def get(self):
self.render("index.html")
def post(self):
username = self.get_argument("username")
password = self.get_argument("password")
user_infos = mrd.select_table(table="users",column="*",condition="username",value=username)
if user_infos:
db_pwd = user_infos[0][2]
if db_pwd == password:
self.write("welcome you: " + username)
else:
self.write("your password was not right.")
else:
self.write("There is no thi user.")
特別注意,在 methods 目錄中,不要缺少了__init__.py
文件,才能在 index.py 中實(shí)現(xiàn) import methods.readdb as mrd
。
代碼修改到這里,看到的結(jié)果是:
http://wiki.jikexueyuan.com/project/start-learning-python/images/30502.png" alt="" />
這是正確輸入用戶名(所謂正確,就是輸入的用戶名和密碼合法,即在數(shù)據(jù)庫(kù)中有該用戶名,且密碼匹配),并提交數(shù)據(jù)后,反饋給前端的歡迎信息。
http://wiki.jikexueyuan.com/project/start-learning-python/images/30503.png" alt="" />
如果輸入的密碼錯(cuò)誤了,則如此提示。
http://wiki.jikexueyuan.com/project/start-learning-python/images/30504.png" alt="" />
這是隨意輸入的結(jié)果,數(shù)據(jù)庫(kù)中無(wú)此用戶。
需要特別說(shuō)明一點(diǎn),上述演示中,數(shù)據(jù)庫(kù)中的用戶密碼并沒(méi)有加密。關(guān)于密碼加密問(wèn)題,后續(xù)要研究。
總目錄 | 上節(jié):用 tornado 做網(wǎng)站 (2) | 下節(jié):用 tornado 做網(wǎng)站 (4)
如果你認(rèn)為有必要打賞我,請(qǐng)通過(guò)支付寶:qiwsir@126.com,不勝感激。