一、Web開發(fā)
Browser/Server模式目前最流行煞烫,簡稱BS架構(gòu)浑此。在BS架構(gòu)下,客戶端只需要瀏覽器滞详,應(yīng)用程序的邏輯和數(shù)據(jù)都存儲(chǔ)在服務(wù)器端凛俱。瀏覽器只需要請(qǐng)求服務(wù)器喘落,獲取Web頁面,并把Web頁面展示給用戶即可最冰。
當(dāng)然,Web頁面也具有極強(qiáng)的交互性稀火。由于Web頁面是用HTML編寫的暖哨,而HTML具備超強(qiáng)的表現(xiàn)力,并且凰狞,服務(wù)器端升級(jí)后篇裁,客戶端無需任何部署就可以使用到新的版本,因此赡若,BS架構(gòu)迅速流行起來达布。
Web開發(fā)發(fā)展階段:
MVC:為了解決直接用腳本語言嵌入HTML導(dǎo)致的可維護(hù)性差的問題,Web應(yīng)用也引入了Model-View-Controller的模式逾冬,來簡化Web開發(fā)黍聂。ASP發(fā)展為ASP.Net,JSP和PHP也有一大堆MVC框架身腻。
選擇Python開發(fā)Web應(yīng)用产还,不但開發(fā)效率高,而且運(yùn)行速度快嘀趟。
二脐区、HTTP協(xié)議簡介
在Web應(yīng)用中,服務(wù)器把網(wǎng)頁傳給瀏覽器她按,實(shí)際上就是把網(wǎng)頁的HTML代碼發(fā)送給瀏覽器牛隅,讓瀏覽器顯示出來。而瀏覽器和服務(wù)器之間的傳輸協(xié)議是HTTP酌泰,所以:
HTML是一種用來定義網(wǎng)頁的文本媒佣,會(huì)HTML,就可以編寫網(wǎng)頁宫莱;
HTTP是在網(wǎng)絡(luò)上傳輸HTML的協(xié)議丈攒,用于瀏覽器和服務(wù)器的通信。
Chrome瀏覽器后授霸,打開Chrome巡验,在菜單中選擇“視圖”,“開發(fā)者”碘耳,“開發(fā)者工具”显设,就可以顯示開發(fā)者工具:
Elements顯示網(wǎng)頁的結(jié)構(gòu),Network顯示瀏覽器和服務(wù)器的通信辛辨。我們點(diǎn)Network捕捂,確保第一個(gè)小紅燈亮著瑟枫,Chrome就會(huì)記錄所有瀏覽器和服務(wù)器之間的通信.
當(dāng)我們?cè)诘刂窓谳斎?code>www.bilibili.com.cn時(shí),瀏覽器將顯示bili的首頁指攒。在這個(gè)過程中慷妙,瀏覽器都干了哪些事情呢?通過Network的記錄允悦,我們就可以知道膝擂。在Network中,定位到第一條記錄隙弛,點(diǎn)擊架馋,右側(cè)將顯示Request Headers,點(diǎn)擊右側(cè)的view source全闷,我們就可以看到瀏覽器發(fā)給bilibili服務(wù)器的請(qǐng)求.
最主要的兩行分析如下叉寂,第一行
GET / HTTP/1.1
GET表示一個(gè)讀取請(qǐng)求,將從服務(wù)器獲得網(wǎng)頁數(shù)據(jù)总珠,/表示URL的路徑屏鳍,URL總是以/開頭,/就表示首頁局服,最后的HTTP/1.1指示采用的HTTP協(xié)議版本是1.1孕蝉。目前HTTP協(xié)議的版本就是1.1,但是大部分服務(wù)器也支持1.0版本腌逢,主要區(qū)別在于1.1版本允許多個(gè)HTTP請(qǐng)求復(fù)用一個(gè)TCP連接降淮,以加快傳輸速度。
從第二行開始搏讶,每一行都類似于Xxx: abcdefg:
Host: www.sina.com.cn
表示請(qǐng)求的域名是www.bilibili.com佳鳖。如果一臺(tái)服務(wù)器有多個(gè)網(wǎng)站,服務(wù)器就需要通過Host來區(qū)分瀏覽器請(qǐng)求的是哪個(gè)網(wǎng)站媒惕。
繼續(xù)往下找到Response Headers系吩,點(diǎn)擊view source,顯示服務(wù)器返回的原始響應(yīng)數(shù)據(jù)
HTTP響應(yīng)分為Header和Body兩部分(Body是可選項(xiàng))妒蔚,我們?cè)贜etwork中看到的Header最重要的幾行如下:
200 OK
200表示一個(gè)成功的響應(yīng)穿挨,后面的OK是說明。失敗的響應(yīng)有404 Not Found:網(wǎng)頁不存在肴盏,500 Internal Server Error:服務(wù)器內(nèi)部出錯(cuò)科盛,等等。
Content-Type: text/html
Content-Type指示響應(yīng)的內(nèi)容菜皂,這里是text/html表示HTML網(wǎng)頁贞绵。請(qǐng)注意,瀏覽器就是依靠Content-Type來判斷響應(yīng)的內(nèi)容是網(wǎng)頁還是圖片恍飘,是視頻還是音樂榨崩。瀏覽器并不靠URL來判斷響應(yīng)的內(nèi)容谴垫,所以,即使URL是http://example.com/abc.jpg母蛛,它也不一定就是圖片翩剪。
HTTP響應(yīng)的Body就是HTML源碼,我們?cè)诓藛螜谶x擇“視圖”彩郊,“開發(fā)者”肢专,“查看網(wǎng)頁源碼”就可以在瀏覽器中直接查看HTML源碼
當(dāng)瀏覽器讀取到bilbili首頁的HTML源碼后,它會(huì)解析HTML焦辅,顯示頁面,然后椿胯,根據(jù)HTML里面的各種鏈接筷登,再發(fā)送HTTP請(qǐng)求給新浪服務(wù)器,拿到相應(yīng)的圖片哩盲、視頻前方、Flash、JavaScript腳本廉油、CSS等各種資源惠险,最終顯示出一個(gè)完整的頁面。所以我們?cè)贜etwork下面能看到很多額外的HTTP請(qǐng)求抒线。
HTTP請(qǐng)求
我們來總結(jié)一下HTTP請(qǐng)求的流程:
步驟1:瀏覽器首先向服務(wù)器發(fā)送HTTP請(qǐng)求班巩,請(qǐng)求包括:
方法:GET還是POST,GET僅請(qǐng)求資源嘶炭,POST會(huì)附帶用戶數(shù)據(jù)抱慌;
路徑:/full/url/path;
域名:由Host頭指定:Host: www.bilibili.com
以及其他相關(guān)的Header眨猎;
如果是POST抑进,那么請(qǐng)求還包括一個(gè)Body,包含用戶數(shù)據(jù)睡陪。
步驟2:服務(wù)器向?yàn)g覽器返回HTTP響應(yīng)寺渗,響應(yīng)包括:
響應(yīng)代碼:200表示成功,3xx表示重定向兰迫,4xx表示客戶端發(fā)送的請(qǐng)求有錯(cuò)誤信殊,5xx表示服務(wù)器端處理時(shí)發(fā)生了錯(cuò)誤;
響應(yīng)類型:由Content-Type指定汁果;
以及其他相關(guān)的Header鸡号;
通常服務(wù)器的HTTP響應(yīng)會(huì)攜帶內(nèi)容,也就是有一個(gè)Body须鼎,包含響應(yīng)的內(nèi)容鲸伴,網(wǎng)頁的HTML源碼就在Body中府蔗。
步驟3:如果瀏覽器還需要繼續(xù)向服務(wù)器請(qǐng)求其他資源,比如圖片汞窗,就再次發(fā)出HTTP請(qǐng)求姓赤,重復(fù)步驟1、2仲吏。
Web采用的HTTP協(xié)議采用了非常簡單的請(qǐng)求-響應(yīng)模式不铆,從而大大簡化了開發(fā)。當(dāng)我們編寫一個(gè)頁面時(shí)裹唆,我們只需要在HTTP請(qǐng)求中把HTML發(fā)送出去誓斥,不需要考慮如何附帶圖片、視頻等许帐,瀏覽器如果需要請(qǐng)求圖片和視頻劳坑,它會(huì)發(fā)送另一個(gè)HTTP請(qǐng)求,因此成畦,一個(gè)HTTP請(qǐng)求只處理一個(gè)資源距芬。
HTTP協(xié)議同時(shí)具備極強(qiáng)的擴(kuò)展性,雖然瀏覽器請(qǐng)求的是http://www.bilibili.com/的首頁循帐,但是bilibili在HTML中可以鏈入其他服務(wù)器的資源框仔,比如<img src="http://i1.sinaimg.cn/home/2013/1008/U8455P30DT20131008135420.png">
,從而將請(qǐng)求壓力分散到各個(gè)服務(wù)器上拄养,并且离斩,一個(gè)站點(diǎn)可以鏈接到其他站點(diǎn),無數(shù)個(gè)站點(diǎn)互相鏈接起來瘪匿,就形成了World Wide Web捐腿,簡稱WWW。
HTTP格式
每個(gè)HTTP請(qǐng)求和響應(yīng)都遵循相同的格式柿顶,一個(gè)HTTP包含Header和Body兩部分茄袖,其中Body是可選的。
HTTP協(xié)議是一種文本協(xié)議嘁锯,所以宪祥,它的格式也非常簡單。HTTP GET請(qǐng)求的格式:
GET /path HTTP/1.1
Header1: Value1
Header2: Value2
Header3: Value3
每個(gè)Header一行一個(gè)家乘,換行符是\r\n蝗羊。
HTTP POST請(qǐng)求的格式:
POST /path HTTP/1.1
Header1: Value1
Header2: Value2
Header3: Value3
body data goes here...
當(dāng)遇到連續(xù)兩個(gè)\r\n時(shí),Header部分結(jié)束仁锯,后面的數(shù)據(jù)全部是Body耀找。
HTTP響應(yīng)的格式:
200 OK
Header1: Value1
Header2: Value2
Header3: Value3
body data goes here...
HTTP響應(yīng)如果包含body,也是通過\r\n\r\n來分隔的。請(qǐng)?jiān)俅巫⒁庖懊ⅲ珺ody的數(shù)據(jù)類型由Content-Type頭來確定蓄愁,如果是網(wǎng)頁,Body就是文本狞悲,如果是圖片撮抓,Body就是圖片的二進(jìn)制數(shù)據(jù)。
當(dāng)存在Content-Encoding時(shí)摇锋,Body數(shù)據(jù)是被壓縮的丹拯,最常見的壓縮方式是gzip,所以荸恕,看到Content-Encoding: gzip時(shí)乖酬,需要將Body數(shù)據(jù)先解壓縮,才能得到真正的數(shù)據(jù)融求。壓縮的目的在于減少Body的大小咬像,加快網(wǎng)絡(luò)傳輸。
具體參數(shù)書目:HTTP圖解(my ibooks)
三双肤、HTML簡介
HTML定義了頁面的內(nèi)容,CSS來控制頁面元素的樣式钮惠,而JavaScript負(fù)責(zé)頁面的交互邏輯茅糜。這里推薦一個(gè)在線學(xué)習(xí)網(wǎng)站w3schools
四、WSGI接口
了解了HTTP協(xié)議和HTML文檔素挽,我們其實(shí)就明白了一個(gè)Web應(yīng)用的本質(zhì)就是:
瀏覽器發(fā)送一個(gè)HTTP請(qǐng)求蔑赘;
服務(wù)器收到請(qǐng)求,生成一個(gè)HTML文檔预明;
服務(wù)器把HTML文檔作為HTTP響應(yīng)的Body發(fā)送給瀏覽器缩赛;
瀏覽器收到HTTP響應(yīng),從HTTP Body取出HTML文檔并顯示撰糠。
所以酥馍,最簡單的Web應(yīng)用就是先把HTML用文件保存好,用一個(gè)現(xiàn)成的HTTP服務(wù)器軟件阅酪,接收用戶請(qǐng)求旨袒,從文件中讀取HTML,返回术辐。Apache砚尽、Nginx、Lighttpd等這些常見的靜態(tài)服務(wù)器就是干這件事情的辉词。
如果要?jiǎng)討B(tài)生成HTML必孤,就需要把上述步驟自己來實(shí)現(xiàn)。不過瑞躺,接受HTTP請(qǐng)求敷搪、解析HTTP請(qǐng)求兴想、發(fā)送HTTP響應(yīng)都是苦力活,如果我們自己來寫這些底層代碼购啄,還沒開始寫動(dòng)態(tài)HTML呢襟企,就得花個(gè)把月去讀HTTP規(guī)范。
正確的做法是底層代碼由專門的服務(wù)器軟件實(shí)現(xiàn)狮含,我們用Python專注于生成HTML文檔顽悼。因?yàn)槲覀儾幌M佑|到TCP連接、HTTP原始請(qǐng)求和響應(yīng)格式几迄,所以蔚龙,需要一個(gè)統(tǒng)一的接口,讓我們專心用Python編寫Web業(yè)務(wù)映胁。
這個(gè)接口就是WSGI:Web Server Gateway Interface木羹。
WSGI接口定義非常簡單,它只要求Web開發(fā)者實(shí)現(xiàn)一個(gè)函數(shù)解孙,就可以響應(yīng)HTTP請(qǐng)求坑填。我們來看一個(gè)最簡單的Web版本的“Hello, web!”:
def application(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/html')])
return [b'<h1>Hello, web!</h1>']
上面的application()
函數(shù)就是符合WSGI標(biāo)準(zhǔn)的一個(gè)HTTP處理函數(shù),它接收兩個(gè)參數(shù):
environ:一個(gè)包含所有HTTP請(qǐng)求信息的dict對(duì)象弛姜;
start_response:一個(gè)發(fā)送HTTP響應(yīng)的函數(shù)脐瑰。
在application()
函數(shù)中,調(diào)用:
start_response('200 OK', [('Content-Type', 'text/html')])
就發(fā)送了HTTP響應(yīng)的Header廷臼,注意Header只能發(fā)送一次苍在,也就是只能調(diào)用一次start_response()
函數(shù)。start_response()
函數(shù)接收兩個(gè)參數(shù)荠商,一個(gè)是HTTP響應(yīng)碼寂恬,一個(gè)是一組list表示的HTTP Header,每個(gè)Header用一個(gè)包含兩個(gè)str的tuple表示莱没。
通常情況下初肉,都應(yīng)該把Content-Type
頭發(fā)送給瀏覽器。其他很多常用的HTTP Header
也應(yīng)該發(fā)送饰躲。
然后朴译,函數(shù)的返回值b'<h1>Hello, web!</h1>'
將作為HTTP響應(yīng)的Body發(fā)送給瀏覽器。
有了WSGI属铁,我們關(guān)心的就是如何從environ
這個(gè)dict對(duì)象拿到HTTP請(qǐng)求信息眠寿,然后構(gòu)造HTML,通過start_response()
發(fā)送Header焦蘑,最后返回Body盯拱。
整個(gè)application()
函數(shù)本身沒有涉及到任何解析HTTP的部分,也就是說,底層代碼不需要我們自己編寫狡逢,我們只負(fù)責(zé)在更高層次上考慮如何響應(yīng)請(qǐng)求就可以了宁舰。
不過,等等奢浑,這個(gè)application()
函數(shù)怎么調(diào)用蛮艰?如果我們自己調(diào)用,兩個(gè)參數(shù)environ
和start_response
我們沒法提供雀彼,返回的bytes也沒法發(fā)給瀏覽器壤蚜。
所以application()
函數(shù)必須由WSGI服務(wù)器來調(diào)用。有很多符合WSGI規(guī)范的服務(wù)器徊哑,我們可以挑選一個(gè)來用袜刷。但是現(xiàn)在,我們只想盡快測(cè)試一下我們編寫的application()
函數(shù)真的可以把HTML輸出到瀏覽器莺丑,所以著蟹,要趕緊找一個(gè)最簡單的WSGI服務(wù)器,把我們的Web應(yīng)用程序跑起來梢莽。
好消息是Python內(nèi)置了一個(gè)WSGI服務(wù)器萧豆,這個(gè)模塊叫wsgiref,它是用純Python編寫的WSGI服務(wù)器的參考實(shí)現(xiàn)昏名。所謂“參考實(shí)現(xiàn)”是指該實(shí)現(xiàn)完全符合WSGI標(biāo)準(zhǔn)涮雷,但是不考慮任何運(yùn)行效率,僅供開發(fā)和測(cè)試使用葡粒。
運(yùn)行WSGI服務(wù)
我們先編寫hello.py份殿,實(shí)現(xiàn)Web應(yīng)用程序的WSGI處理函數(shù):
# hello.py
def application(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/html')])
return [b'<h1>Hello, web!</h1>']
然后膜钓,再編寫一個(gè)server.py
嗽交,負(fù)責(zé)啟動(dòng)WSGI服務(wù)器,加載application()
函數(shù):
# server.py
# 從wsgiref模塊導(dǎo)入:
from wsgiref.simple_server import make_server
# 導(dǎo)入我們自己編寫的application函數(shù):
from hello import application
# 創(chuàng)建一個(gè)服務(wù)器颂斜,IP地址為空夫壁,端口是8000,處理函數(shù)是application:
httpd = make_server('', 8000, application)
print('Serving HTTP on port 8000...')
# 開始監(jiān)聽HTTP請(qǐng)求:
httpd.serve_forever()
確保以上兩個(gè)文件在同一個(gè)目錄下沃疮,然后在命令行輸入python server.py
來啟動(dòng)WSGI服務(wù)器:
啟動(dòng)成功后盒让,打開瀏覽器,輸入http://localhost:8000/司蔬,就可以看到結(jié)果了:
小結(jié)
無論多么復(fù)雜的Web應(yīng)用程序邑茄,入口都是一個(gè)WSGI處理函數(shù)。HTTP請(qǐng)求的所有輸入信息都可以通過environ獲得俊啼,HTTP響應(yīng)的輸出都可以通過
start_response()
加上函數(shù)返回值作為Body肺缕。
復(fù)雜的Web應(yīng)用程序,光靠一個(gè)WSGI函數(shù)來處理還是太底層了,我們需要在WSGI之上再抽象出Web框架同木,進(jìn)一步簡化Web開發(fā)浮梢。
五、使用Web框架
了解了WSGI框架彤路,我們發(fā)現(xiàn):其實(shí)一個(gè)Web App秕硝,就是寫一個(gè)WSGI的處理函數(shù),針對(duì)每個(gè)HTTP請(qǐng)求進(jìn)行響應(yīng)洲尊。
但是如何處理HTTP請(qǐng)求不是問題远豺,問題是如何處理100個(gè)不同的URL。
每一個(gè)URL可以對(duì)應(yīng)GET和POST請(qǐng)求颊郎,當(dāng)然還有PUT憋飞、DELETE等請(qǐng)求,但是我們通常只考慮最常見的GET和POST請(qǐng)求姆吭。
一個(gè)最簡單的想法是從environ
變量里取出HTTP請(qǐng)求的信息榛做,然后逐個(gè)判斷:
def application(environ, start_response):
method = environ['REQUEST_METHOD']
path = environ['PATH_INFO']
if method=='GET' and path=='/':
return handle_home(environ, start_response)
if method=='POST' and path='/signin':
return handle_signin(environ, start_response)
...
只是這么寫下去代碼是肯定沒法維護(hù)了。
代碼這么寫沒法維護(hù)的原因是因?yàn)閃SGI提供的接口雖然比HTTP接口高級(jí)了不少内狸,但和Web App的處理邏輯比检眯,還是比較低級(jí),我們需要在WSGI接口之上能進(jìn)一步抽象昆淡,讓我們專注于用一個(gè)函數(shù)處理一個(gè)URL锰瘸,至于URL到函數(shù)的映射,就交給Web框架來做昂灵。
由于用Python開發(fā)一個(gè)Web框架十分容易避凝,所以Python有上百個(gè)開源的Web框架。這里我們先不討論各種Web框架的優(yōu)缺點(diǎn)眨补,直接選擇一個(gè)比較流行的Web框架——Flask來使用管削。
用Flask編寫Web App比WSGI接口簡單,我們先用pip安裝Flask,然后寫一個(gè)app.py
,處理3個(gè)URL撑螺,分別是:
GET /:
首頁含思,返回Home;
GET /signin:
登錄頁甘晤,顯示登錄表單含潘;
POST /signin:
處理登錄表單,顯示登錄結(jié)果线婚。
注意噢遏弱,同一個(gè)URL/signin
分別有GET和POST兩種請(qǐng)求,映射到兩個(gè)處理函數(shù)中塞弊。
Flask通過Python的裝飾器在內(nèi)部自動(dòng)地把URL和函數(shù)給關(guān)聯(lián)起來漱逸,所以缀踪,我們寫出來的代碼就像這樣:
from flask import Flask
from flask import request
app = Flask(__name__)
@app.route('/', methods=['GET', 'POST']) # 裝飾器如何關(guān)聯(lián)?可以提示對(duì)應(yīng)的URL虹脯?
def home():
return '<h1>Home</h1>'
@app.route('/signin', methods=['GET'])
def signin_form():
return '''<form action="/signin" method="post">
<p><input name="username"></p>
<p><input name="password" type="password"></p>
<p><button type="submit">Sign In</button></p>
</form>'''
@app.route('/signin', methods=['POST'])
def signin():
# 需要從request對(duì)象讀取表單內(nèi)容:
if request.form['username']=='admin' and request.form['password']=='password':
return '<h3>Hello, admin!</h3>'
return '<h3>Bad username or password.</h3>'
if __name__ == '__main__':
app.run()
運(yùn)行python app.py驴娃,F(xiàn)lask自帶的Server在端口5000上監(jiān)聽:
$ python app.py
* Running on http://127.0.0.1:5000/
打開瀏覽器,輸入首頁地址http://localhost:5000/,首頁顯示正確
再在瀏覽器地址欄輸入http://localhost:5000/signin循集,會(huì)顯示登錄表單
輸入預(yù)設(shè)的用戶名admin和口令password唇敞,登錄成功
小結(jié)
有了Web框架,我們?cè)诰帉慦eb應(yīng)用時(shí)咒彤,注意力就從WSGI處理函數(shù)轉(zhuǎn)移到URL+對(duì)應(yīng)的處理函數(shù)疆柔,這樣,編寫Web App就更加簡單了镶柱。
在編寫URL處理函數(shù)時(shí)旷档,除了配置URL外,從HTTP請(qǐng)求拿到用戶數(shù)據(jù)也是非常重要的歇拆。Web框架都提供了自己的API來實(shí)現(xiàn)這些功能鞋屈。Flask通過request.form['name']
來獲取表單的內(nèi)容。
六故觅、使用模板
這就是傳說中的MVC:Model-View-Controller厂庇,中文名“模型-視圖-控制器”。
Python處理URL的函數(shù)就是C:Controller输吏,Controller負(fù)責(zé)業(yè)務(wù)邏輯权旷,比如檢查用戶名是否存在,取出用戶信息等等贯溅;
包含變量{{ name }}
的模板就是V:View拄氯,View負(fù)責(zé)顯示邏輯,通過簡單地替換一些變量它浅,View最終輸出的就是用戶看到的HTML译柏。
MVC中的Model在哪?Model是用來傳給View的罚缕,這樣View在替換變量的時(shí)候艇纺,就可以從Model中取出相應(yīng)的數(shù)據(jù)怎静。
上面的例子中邮弹,Model就是一個(gè)dict:
{ 'name': 'Michael' }
只是因?yàn)镻ython支持關(guān)鍵字參數(shù),很多Web框架允許傳入關(guān)鍵字參數(shù)蚓聘,然后腌乡,在框架內(nèi)部組裝出一個(gè)dict作為Model。
現(xiàn)在夜牡,我們把上次直接輸出字符串作為HTML的例子用高端大氣上檔次的MVC模式改寫一下:
from flask import Flask, request, render_template
app = Flask(__name__)
@app.route('/', methods=['GET', 'POST'])
def home():
return render_template('home.html')
@app.route('/signin', methods=['GET'])
def signin_form():
return render_template('form.html')
@app.route('/signin', methods=['POST'])
def signin():
username = request.form['username']
password = request.form['password']
if username=='admin' and password=='password':
return render_template('signin-ok.html', username=username)
return render_template('form.html', message='Bad username or password', username=username)
if __name__ == '__main__':
app.run()
Flask通過render_template()
函數(shù)來實(shí)現(xiàn)模板的渲染与纽。和Web框架類似侣签,Python的模板也有很多種。Flask默認(rèn)支持的模板是jinja2急迂,所以我們先直接安裝jinja2:
$ pip install jinja2
然后影所,開始編寫jinja2模板:
home.html
用來顯示首頁的模板:
<html>
<head>
<title>Home</title>
</head>
<body>
<h1 style="font-style:italic">Home</h1>
</body>
</html>
form.html -------- # 用來顯示登錄表單的模板,前端代碼省略
signin-ok.html --------- # 登錄成功的模板僚碎,前端代碼省略
登錄失敗的模板呢猴娩?我們?cè)趂orm.html中加了一點(diǎn)條件判斷,把form.html重用為登錄失敗的模板勺阐。
最后卷中,一定要把模板放到正確的templates目錄下,templates和app.py在同級(jí)目錄下:
啟動(dòng)
python app.py
渊抽,看看使用模板的頁面效果:使用模板的另一大好處是蟆豫,模板改起來很方便,而且懒闷,改完保存后十减,刷新瀏覽器就能看到最新的效果,這對(duì)于調(diào)試HTML愤估、CSS和JavaScript的前端工程師來說實(shí)在是太重要了嫉称。
在Jinja2模板中,我們用{{ name }}表示一個(gè)需要替換的變量灵疮。很多時(shí)候织阅,還需要循環(huán)、條件判斷等指令語句震捣,在Jinja2中荔棉,用{% ... %}
表示指令。
比如循環(huán)輸出頁碼:
{% for i in page_list %}
<a href="/page/{{ i }}">{{ i }}</a>
{% endfor %}
如果page_list是一個(gè)list:[1, 2, 3, 4, 5]蒿赢,上面的模板將輸出5個(gè)超鏈接润樱。
小結(jié)
有了MVC,我們就分離了Python代碼和HTML代碼羡棵。HTML代碼全部放到模板里壹若,寫起來更有效率。