python-復(fù)盤-Web開發(fā)

一、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ì)就是:

  1. 瀏覽器發(fā)送一個(gè)HTTP請(qǐng)求蔑赘;

  2. 服務(wù)器收到請(qǐng)求,生成一個(gè)HTML文檔预明;

  3. 服務(wù)器把HTML文檔作為HTTP響應(yīng)的Body發(fā)送給瀏覽器缩赛;

  4. 瀏覽器收到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ù)environstart_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代碼全部放到模板里壹若,寫起來更有效率。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末皂冰,一起剝皮案震驚了整個(gè)濱河市店展,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌秃流,老刑警劉巖赂蕴,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異舶胀,居然都是意外死亡概说,警方通過查閱死者的電腦和手機(jī)碧注,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來糖赔,“玉大人萍丐,你說我怎么就攤上這事》诺洌” “怎么了碉纺?”我有些...
    開封第一講書人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長刻撒。 經(jīng)常有香客問我骨田,道長,這世上最難降的妖魔是什么声怔? 我笑而不...
    開封第一講書人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任态贤,我火速辦了婚禮,結(jié)果婚禮上醋火,老公的妹妹穿的比我還像新娘悠汽。我一直安慰自己,他們只是感情好芥驳,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開白布柿冲。 她就那樣靜靜地躺著,像睡著了一般兆旬。 火紅的嫁衣襯著肌膚如雪假抄。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評(píng)論 1 301
  • 那天丽猬,我揣著相機(jī)與錄音宿饱,去河邊找鬼。 笑死脚祟,一個(gè)胖子當(dāng)著我的面吹牛谬以,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播由桌,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼为黎,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了行您?” 一聲冷哼從身側(cè)響起铭乾,我...
    開封第一講書人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎邑雅,沒想到半個(gè)月后片橡,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體妈经,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡淮野,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年捧书,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片骤星。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡经瓷,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出洞难,到底是詐尸還是另有隱情舆吮,我是刑警寧澤,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布队贱,位于F島的核電站色冀,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏柱嫌。R本人自食惡果不足惜锋恬,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望编丘。 院中可真熱鬧与学,春花似錦、人聲如沸嘉抓。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽抑片。三九已至卵佛,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間敞斋,已是汗流浹背级遭。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留渺尘,地道東北人挫鸽。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像鸥跟,于是被迫代替她去往敵國和親丢郊。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理医咨,服務(wù)發(fā)現(xiàn)枫匾,斷路器,智...
    卡卡羅2017閱讀 134,654評(píng)論 18 139
  • 一拟淮、概念(載錄于:http://www.cnblogs.com/EricaMIN1987_IT/p/3837436...
    yuantao123434閱讀 8,353評(píng)論 6 152
  • API定義規(guī)范 本規(guī)范設(shè)計(jì)基于如下使用場景: 請(qǐng)求頻率不是非常高:如果產(chǎn)品的使用周期內(nèi)請(qǐng)求頻率非常高干茉,建議使用雙通...
    有涯逐無涯閱讀 2,539評(píng)論 0 6
  • Http協(xié)議詳解 標(biāo)簽(空格分隔): Linux 聲明:本片文章非原創(chuàng),內(nèi)容來源于博客園作者M(jìn)IN飛翔的HTTP協(xié)...
    Sivin閱讀 5,223評(píng)論 3 82
  • “感覺身體被掏空”沾谓,一天之內(nèi),很多人都被這首合唱神曲刷屏了: 這首神曲于7月27日上午9點(diǎn)40分在公眾號(hào)“上海彩虹...
    beckywang711閱讀 506評(píng)論 0 0