用tornado做網(wǎng)站(3)

2018-02-24 15:48 更新

數(shù)據(jù)傳輸

在已經(jīng)建立了前端表單之后,就要實現(xiàn)前端和后端之間的數(shù)據(jù)傳遞。在工程中,常用到一個被稱之為ajax()的方法。

關(guān)于ajax的故事,需要濃墨重彩,因為它足夠精彩。

ajax是“Asynchronous Javascript and XML”(異步JavaScript和XML)的縮寫,在它的發(fā)展歷程中,匯集了眾家貢獻。比如微軟的IE團隊曾經(jīng)將XHR(XML HttpRequest)用于web瀏覽器和web服務(wù)器間傳輸數(shù)據(jù),并且被W3C標準采用。當然,也有其它公司為Ajax技術(shù)做出了貢獻,雖然它們都被遺忘了,比如Oddpost,后來被Yahoo!收購并成為Yahoo! Mail的基礎(chǔ)。但是,真正讓Ajax大放異彩的google是不能被忽視的,正是google在Gmail、Suggest和Maps上大規(guī)模使用了Ajax,才使得人們看到了它的魅力,程序員由此而興奮。

技術(shù)總是在不斷進化的,進化的方向就是用著越來越方便。

回到上一節(jié)使用的jQuery,里面就有ajax()方法,能夠讓程序員方便的調(diào)用。

ajax()方法通過 HTTP 請求加載遠程數(shù)據(jù)。

該方法是jQuery底層AJAX實現(xiàn)。簡單易用的高層實現(xiàn)見$.get, $.post等。$.ajax() 返回其創(chuàng)建的 XMLHttpRequest 對象。大多數(shù)情況下你無需直接操作該函數(shù),除非你需要操作不常用的選項,以獲得更多的靈活性。

最簡單的情況下,$.ajax() 可以不帶任何參數(shù)直接使用。

在上文介紹Ajax的時候,用到了一個重要的術(shù)語——“異步”,與之相對應(yīng)的叫做“同步”。我引用來自阮一峰的網(wǎng)絡(luò)日志中的通俗描述:

"同步模式"就是上一段的模式,后一個任務(wù)等待前一個任務(wù)結(jié)束,然后再執(zhí)行,程序的執(zhí)行順序與任務(wù)的排列順序是一致的、同步的;"異步模式"則完全不同,每一個任務(wù)有一個或多個回調(diào)函數(shù)(callback),前一個任務(wù)結(jié)束后,不是執(zhí)行后一個任務(wù),而是執(zhí)行回調(diào)函數(shù),后一個任務(wù)則是不等前一個任務(wù)結(jié)束就執(zhí)行,所以程序的執(zhí)行順序與任務(wù)的排列順序是不一致的、異步的。

"異步模式"非常重要。在瀏覽器端,耗時很長的操作都應(yīng)該異步執(zhí)行,避免瀏覽器失去響應(yīng),最好的例子就是Ajax操作。在服務(wù)器端,"異步模式"甚至是唯一的模式,因為執(zhí)行環(huán)境是單線程的,如果允許同步執(zhí)行所有http請求,服務(wù)器性能會急劇下降,很快就會失去響應(yīng)。

看來,ajax()是前后端進行數(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值,放到一個json對象中(關(guān)于json,請閱讀《標準庫(8)》),形成了一個json對象。接下來就是利用ajax()方法將這個json對象傳給后端。

jQuery中的ajax()方法使用比較簡單,正如上面代碼所示,只需要$.ajax()即可,不過需要對立面的參數(shù)進行說明。

  • type:post還是get。關(guān)于post和get的區(qū)別,可以閱讀:HTTP POST GET 本質(zhì)區(qū)別詳解
  • url:post或者get的地址
  • data:傳輸?shù)臄?shù)據(jù),包括三種:(1)html拼接的字符串;(2)json數(shù)據(jù);(3)form表單經(jīng)serialize()序列化的。本例中傳輸?shù)木褪莏son數(shù)據(jù),這也是經(jīng)常用到的一種方式。
  • cache:默認為true,如果不允許緩存,設(shè)置為false.
  • success:請求成功時執(zhí)行回調(diào)函數(shù)。本例中,將返回的data用alert方式彈出來。讀者是否注意到,我在很多地方都用了alert()這個東西,目的在于調(diào)試,走一步看一步,看看得到的數(shù)據(jù)是否如自己所要。也是有點不自信呀。
  • error:如果請求失敗所執(zhí)行的函數(shù)。

后端接受數(shù)據(jù)

前端通過ajax技術(shù),將數(shù)據(jù)已json格式傳給了后端,并且指明了對象目錄"/",這個目錄在url.py文件中已經(jīng)做了配置,是由handlers目錄的index.py文件的IndexHandler類來出來。因為是用post方法傳的數(shù)據(jù),那么在這個類中就要有post方法來接收數(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ù)來接收前端傳過來的數(shù)據(jù),這個函數(shù)的完整格式是get_argument(name, default=[], strip=True),它能夠獲取name的值。在上面的代碼中,name就是從前端傳到后端的那個json對象的鍵的名字,是哪個鍵就獲取該鍵的值。如果獲取不到name的值,就返回default的值,但是這個值默認是沒有的,如果真的沒有就會拋出HTTP 400。特別注意,在get的時候,通過get_argument()函數(shù)獲得url的參數(shù),如果是多個參數(shù),就獲取最后一個的值。要想獲取多個值,可以使用get_arguments(name, strip=true)。

上例中分別用get_argument()方法得到了username和password,并且它們都是unicode編碼的數(shù)據(jù)。

tornado.web.RequestHandler的方法write(),即上例中的self.write(username),是后端向前端返回數(shù)據(jù)。這里返回的實際上是一個字符串,也可返回json字符串。

如果讀者要查看修改代碼之后的網(wǎng)站效果,最有效的方式先停止網(wǎng)站(ctrl+c),在從新執(zhí)行python server.py運行網(wǎng)站,然后刷新瀏覽器即可。這是一種較為笨拙的方法。一種靈巧的方法是開啟調(diào)試模式。是否還記得?在設(shè)置setting的時候,寫上debug = True就表示是調(diào)試模式了(參閱:用tornado做網(wǎng)站(1))。但是,調(diào)試模式也不是十全十美,如果修改模板,就不會加載,還需要重啟服務(wù)。反正重啟也不麻煩,無妨啦。

看看上面的代碼效果:

這是前端輸入了用戶名和密碼之后,點擊login按鈕,提交給后端,后端再向前端返回數(shù)據(jù)之后的效果。就是我們想要的結(jié)果。

驗證用戶名和密碼

按照流程,用戶在前端輸入了用戶名和密碼,并通過ajax提交到了后端,后端借助于get_argument()方法得到了所提交的數(shù)據(jù)(用戶名和密碼)。下面要做的事情就是驗證這個用戶名和密碼是否合法,其體現(xiàn)在:

  • 數(shù)據(jù)庫中是否有這個用戶
  • 密碼和用戶先前設(shè)定的密碼(已經(jīng)保存在數(shù)據(jù)庫中)是否匹配

這個驗證工作完成之后,才能允許用戶登錄,登錄之后才能繼續(xù)做某些事情。

首先,在methods目錄中(已經(jīng)有了一個db.py)創(chuàng)建一個文件,我命名為readdb.py,專門用來存儲讀數(shù)據(jù)用的函數(shù)(這種劃分完全是為了明確和演示一些應(yīng)用方法,讀者也可以都寫到db.py中)。這個文件的代碼如下:

#!/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

上面這段代碼,建議讀者可以寫上注釋,以檢驗自己是否能夠?qū)⒁酝闹R融會貫通地應(yīng)用。恕我不再解釋。

有了這段代碼之后,就進一步改寫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中實現(xiàn)import methods.readdb as mrd。

代碼修改到這里,看到的結(jié)果是:

這是正確輸入用戶名(所謂正確,就是輸入的用戶名和密碼合法,即在數(shù)據(jù)庫中有該用戶名,且密碼匹配),并提交數(shù)據(jù)后,反饋給前端的歡迎信息。

如果輸入的密碼錯誤了,則如此提示。

這是隨意輸入的結(jié)果,數(shù)據(jù)庫中無此用戶。

需要特別說明一點,上述演示中,數(shù)據(jù)庫中的用戶密碼并沒有加密。關(guān)于密碼加密問題,后續(xù)要研究。

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號