前面的學習回顧:css
接下來咱們要作的是利用以前的search_for_letters函數,讓人們能夠在web瀏覽器上訪問這個函數提供的服務。關於這個函數的內容在前面的隨筆有介紹。html
def search_for_letters(phrase: str, letters: str) -> set: '''FUNC:return a set of 'letters' found in 'phrase' . '''
return set(letters).intersection(set(phrase))
不管在web上作什麼,都與請求和響應有關。Web請求做爲用戶交互的結果從一個web瀏覽器發送到一個web服務器。在web服務器上,會造成一個web響應(或應答),並返回給web瀏覽器。整個過程能夠總結爲五步:python
這時候有;兩種可能性。程序員
若是web請求只是請求一個靜態的內容,如一個HTML文件、圖像或存儲在web服務器硬盤上的其餘資源,web服務器會找到這個資源,準備把它做爲一個web響應返回給web瀏覽器。web
若是請求的是動態內容,也就是說必須生成的內容,若是搜索結果是一個在線購物車的當前內容,web服務器會運行一些代碼來生成web響應。flask
咱們想讓web實現的功能瀏覽器
讓web瀏覽器與咱們的web應用交互。在瀏覽器的地址欄輸入這個微博應用的URL地址來訪問應用的服務。瀏覽器上會出現一個頁面,要求用戶提供search_for_letters函數所須要的參數。一旦輸入參數,用戶點擊一個按鈕就會看到結果。服務器
回憶這個函數的定義,它指出這個函數須要至少一個(至多兩個)參數:phrase和letters(要在phrase中搜索letters,letters是可選的,由於它的默認值aeiou)數據結構
def search_for_letters(phrase: str, letters: str)->set:app
關於這web頁面什麼樣,下面是我畫出的草圖:
當用戶單擊DO IT! 時,瀏覽器將這個數據發送給正在等待的web服務器,它會抽取phrase和letters的值,而後等待用戶調用search_for_letters函數。
咱們須要作的事情
要構建一個能實際運行的服務器端web應用,還須要瞭解web應用框架,它提供了一組通用的基礎技術,能夠基於這些技術構建web應用。在這裏直接選擇一個名爲Flask的流行框架,繼續咱們的工做。
Flask是一個第三方模塊,與標準庫模塊不一樣,須要先安裝才能導入和使用。
Python社區維護了一個集中管理第三方模塊的網站,名爲PyPI(Python Package Index)
依舊是使用管理員運行cmd
python -m pip install flask
順便安裝了四個模塊如圖中所示,這些是flask的依賴包。
Flask提供了一組模塊能夠幫助構建服務器端web應用。從理論上來說,這是一個微web框架,比較全面的web框架是Django,不過咱們的需求不用這個複雜的框架,flask徹底可以勝任如今的需求。
建立一個新文件保存爲hello_flask.py 放在一個名爲webapp的文件夾中。
from flask import Flask app = Flask(__name__) #注意此處是兩個下劃線
@app.route('/') def hello() -> str: return 'hello world from flask' app.run()
這是在那個文件夾裏面同時按住shift和點擊右鍵,會出如今此處打開命令窗口,這樣一來能夠免去切換目錄的問題。
輸入Python hello_flask.py會出現:
看到最後一行的內容表示一切正常,表示flask的服務器已經準備就緒,正在等待。如今打開一個你喜歡的瀏覽器,輸入剛纔消息裏面的URL地址:
http://127.0.0.1:5000/
瀏覽器窗口出現了剛纔return後的內容,如今再來看剛纔的web應用終端窗口,出現了新的狀態消息:
from flask import Flask
‘’’ 模塊名 類名
這裏也能夠直接寫import flask ,而後用flask.Flask指示類,不過這個方法在這裏可讀性很差。’’’
app = Flask(__name__)
‘’’建立一個Flask類型的對象,把它賦給app變量。
__name__是什麼?
這個值由Python解釋器維護,在程序代碼中任何地方使用這個值時,它會設置爲當前活動模塊的名字。
Python中把__name__叫作dunder name ‘’’
@app.route('/')#函數修飾符,有一個@前綴。
#函數修飾符能夠調整一個已有的函數的行爲,而不修改函數的代碼!
‘’’這個web應用代碼中,app變量使用Flask的route修飾符。Route容許你將一個URL web路徑與一個已有的Python函數相關聯。在這裏URL ‘/’將與下一行代碼中定義的函數關聯,這個函數名爲hello。當一個指向‘/’URL的請求到達服務器時,route修飾符會安排Flask web服務器調用這個函數,而後route修飾符會等待所修飾的函數生成的輸出,再將輸出返回給服務器,而後服務器再將輸出返回給正在等待的web瀏覽器。’’’
def hello() -> str:
return 'hello world from Flask!'
#這個函數返回一個字符串,調用時返回消息:hello world from Flask!
app.run()
#讓web應用開始運行
此時,Flask會啓動它的內置web服務器,並在這個服務器中運行你的web應用代碼。Web服務器接收到指向‘/’URL的請求時,會響應「hello world from Flask!」消息,而指向其餘URL請求會獲得一個404 NOT FOUND錯誤消息。
而終端窗口中運行的web應用也會有一個消息更新狀態:
接下來修改hello_flask.py來包含第二個URL:/search
編寫代碼使這個URL與一個名爲do_search函數相關聯,它會調用search_for_letters函數(從vsearch模塊中)而後讓do_search函數返回搜素時肯定的結果,在這裏要用短語「life, the universe, and everything!」中搜索字符串「eriu,!」。
新的代碼:
1 from flask import Flask 2 from vsearch import search_for_letters 3
4 app = Flask(__name__) 5
6 @app.route('/') 7 def hello() -> str: 8 return 'hello world from Flask'
9
10 @app.route('/search') 11 def do_search() -> str: 12 return str(search_for_letters('life,the universe,and everything!','eriu,!')) 13
14 app.run()
在命令提示符上終止剛纔的終端(Ctrl+C),而後按向上的那個鍵,從新開啓一個web應用。如今處於等待狀態,而後在;瀏覽器輸入剛纔定義的新的URL路徑。http://127.0.0.1:5000/search
瀏覽器出現了調用函數search_for_letters的結果:
在這裏,127.0.0.1是localhost,也成爲本地環回地址,而5000是協議端口號,是Flask默認的端口。
咱們如今只是實現了一部分的任務,回想最開始的目的,還記得以前的草圖嗎?咱們但願有一個web頁面接收輸入,另外還須要一個web頁面顯示結果。
像草圖中這樣的頁面,而不是{'e', 'i', '!', 'u', 'r', ','}。
利用模板引擎,程序員能夠應用面向對象的繼承和重用概念來生成文本數據,如web頁面。
網站的外觀能夠在一個頂層HTML模板中定義,這稱爲基模板,而後其餘HTML頁面繼承這個模板。若是對基模板進行修改,那麼這個修改就會體如今全部繼承這個基模板的HTML頁面中體現。
Flask提供的模板引擎名爲Jinja2,這裏只做簡單的介紹,爲咱們所用的三個模板:base.html entry.html results.html
三個模板能夠從這裏下載http://python.itcarlow.ie/ed2/ch05/templates/
這個網址是我學習的書《Head First Python》提供的。
關於base.html
關於entry.html
用戶能夠與這個HTML表單交互來提供web應用所須要的phrase和letters值。這個模板繼承了基模板,爲名爲body的塊提供了一個替代塊。
關於results.html
這個文件用來呈現搜索結果
在這裏,對HTML的內容不作多的介紹,如今大概瞭解了它的一些基礎內容,會用便可。
Flask提供了一個名爲render_template的函數,若是指定一個模板名和所需的參數,調用這個函數時會返回一個HTML串。爲了使用render_template,要把這個函數名增長到從flask模板導入的函數列表中,而後根據須要調用這個函數。把以前的web應用代碼文件hello_flask.py重命名一個更適合的名字:vsearch_for_web.py。並對該代碼進行如下修改:
from flask import Flask , render_template
@app.route(‘/entry’)
def entry_page() -> ‘html’:
return render_template(‘entry.html’, the_title = ‘Welcome to search_for_letters on the web !’)
改完的代碼以下:
1 from flask import Flask, render_template 2 from vsearch import search_for_letters 3
4 app = Flask(__name__) 5
6 @app.route('/') 7 def hello() -> str: 8 return 'hello world from Flask
9
10 @app.route('/search') 11 def do_search() -> str: 12 return str(search_for_letters('life, the universe, and everything!', 'eriu,!')) 13
14 @app.route('/entry') 15 def entry_page() -> 'html': 16 return render_template('entry.html', the_title = 'Welcome to search_for_letters on the web!') 17
18 app.run()
須要在webapp文件夾裏面建立以下的內容,這些內容均可以在剛纔提供的網站裏下載
Static文件夾裏有一個名爲hf.css的文件
如今已經準備好了,回到以前的命令提示符窗口(就是那個在webapp文件裏面按住shift和右鍵而後點擊今後處運行的那個窗口),執行新的代碼:
python vsearch_for_web.py
窗口像剛纔那樣出現了running
如今使用新的URL:http://127.0.0.1:5000/entry
瀏覽器出現了相似於草圖的窗口:
第一個對應/entry請求,第二個是對應瀏覽器對hf.css樣式表的請求有關。
雖然這個頁面看起來有些不美觀,可是咱們如今先研究它能不能完成須要的功能。
如今輸入一個短語單擊do it!發生了一個錯誤:
看到服務器端窗口出現了一個內容:
HTTP是容許web瀏覽器和服務器通訊的協議。每一個web請求都會生成一個http狀態碼響應。
狀態碼主要有五類:100類、200類、300類、400類和500類。
100~199範圍內的狀態碼是信息消息:服務器在提供關於客戶端請求的詳細信息。
200~299範圍內的狀態碼是成功消息:服務器已經接收、理解和處理客戶端的請求,一切正常。
300~399範圍內的狀態碼是重定向消息:服務器通知客戶端請求能夠在別到處理。
400~499範圍內的狀態碼是客戶端錯誤消息:服務器從客戶端接收到一個它不理解也沒法處理的請求,一般是客戶端的問題。
500~599範圍內的狀態碼是服務器錯誤消息:服務器從客戶端接收到一個請求,可是服務器嘗試處理這個請求時失敗了,一般是服務器的問題。
瀏覽器一般使用GET方法從web服務器請求一個資源,這是HTTP默認的方法。
POST方法容許web瀏覽器向服務器經過HTTP發送數據,這與HTML<form>標記相關聯。
可讓flask web應用從瀏覽器接收提交的數據,爲此要在@app.route行上提供一個額外的參數——methods。
下面對vsearch_for_web.py中的代碼進行修改:第十行增長了POST方法
這與entry.html文件中的第7行代碼對應,注意參數名method的單數複數形式。
下面打開調試模式,將最後一行的代碼改成:app.run(debug=True)
如今咱們的代碼應該以下所示:
1 from flask import Flask, render_template 2 from vsearch import search_for_letters 3
4 app = Flask(__name__) 5
6 @app.route('/') 7 def hello() -> str: 8 return 'hello world from Flask'
9
10 @app.route('/search', methods = ['POST']) 11 def do_search() -> str: 12 return str(search_for_letters('life,the universe,and everything!','eriu,!')) 13
14 @app.route('/entry') 15 def entry_page() -> 'html': 16 return render_template('entry.html', the_title = 'Welcome to search_for_letters on the web!') 17
18 app.run(debug=True)
記住每次調試時都須要把以前打開的客戶端窗口從新啓動一次,如今已是調試模式了:
下來試試咱們的web應用交互:
點擊DO IT時會看到不管以前的phrase輸入什麼,都會出現一個相同的結果:
這是由於以前代碼中有一些硬編碼值:life,the universe,and everything!','eriu,!
因此咱們須要修改web應用的代碼來接收數據,而後才能進行新的操做。
Flask提供了一個內置對象:request,利用這個對象能夠訪問所提交的數據。request對象包含一個名爲form的字典屬性,form支持中括號記法,即要訪問表單中的一個數據能夠把表單元素的名字放在中括號中:request.form[‘phrase’]和request.form[‘letters’]
這個對象仍須要導入才能使用。如下是新增長的內容:
第一行:
from flask import Flask, render_template, request
第十行:
@app.route('/search', methods = ['POST'])
def do_search() -> str:
phrase = request.form['phrase']
letters = request.form['letters']
return str(search_for_letters(phrase, letters))
看一看命令提示窗口,flask調試器發現代碼有更改,會重啓web應用……
set()表示沒有搜索到letters
咱們但願結果也生成一個相似的HTML表單。
回顧一下results.html模板的內容:
有四個變量:the_title,the_phrase,the_letters,the_results
對代碼進行修改:
def do_search() -> 'html':
phrase = request.form['phrase']
letters = request.form['letters']
title = 'Here are your results:'
results = str(search_for_letters(phrase, letters))
return render_template('results.html', the_phrase = phrase, the_letters = letters, the_title = title, the_results = results, )
保存後在測試,發現結果頁面已經生成了HTML表單
就目前來看咱們已經成功的構建了一個web應用,還使用了交互的HTML頁面,如今這個版本的web應用支持三個URL:/, /search, /entry
/地址會返回一個hello world from Flask消息,可是若是刪除這一個URL,再請求/時則會出現一個404 not found 錯誤(找不到對象確實聽起來不是很舒服),下面讓Flask把對/URL的全部請求重定向到/entry URL。調整hello函數是必要的。
重定向:
在第一行導入列表增長redirect;
修改hello函數:
這樣一來訪問http://127.0.0.1:5000/和http://127.0.0.1:5000/entry都是返回一個頁面。可是這個時候,一個請求指向/ URL,咱們的web應用首先響應一個302重定向,而後web瀏覽器會發送另外一個請求指向/entry URL,這會成功獲得web應用的服務,可是每次指向/的一個請求就會變成兩個請求,顯然這樣是浪費資源的。
函數能夠有多個URL
刪除hello函數,把@app.route('/')剪切到下面的@app.route('/entry')上面。
這樣就解決了剛纔的浪費問題。
1 from flask import Flask, render_template, request, redirect 2 from vsearch import search_for_letters 3
4 app = Flask(__name__) 5
6 @app.route('/search', methods = ['POST']) 7 def do_search() -> 'html': 8 phrase = request.form['phrase'] 9 letters = request.form['letters'] 10 title = 'Here are your results:'
11 results = str(search_for_letters(phrase, letters)) 12 return render_template('results.html', the_phrase = phrase, the_letters = letters, the_title = title, the_results = results, ) 13
14 @app.route('/') 15 @app.route('/entry') 16 def entry_page() -> 'html': 17 return render_template('entry.html', the_title = 'Welcome to search_for_letters on the web!') 18
19 app.run(debug=True)