MiniWEB項(xiàng)目琅坡、模版悉患、使用正則進(jìn)行替換 {\%content\%} 為內(nèi)容、路由榆俺、裝飾器實(shí)現(xiàn)路由购撼、AOP面向切面編程、SEO搜索引擎優(yōu)化 實(shí)現(xiàn)偽靜態(tài)服務(wù)器
3.2 MiniWEB項(xiàng)目
學(xué)習(xí)目標(biāo)
? 1. 能夠說(shuō)出路由的概念
? 2. 能夠說(shuō)出什么是面向切片編程
? 3. 能夠說(shuō)出模板文件的使用
--------------------------------------------------------------------------------
3.2.1 實(shí)現(xiàn)框架
在這部分內(nèi)容中主要實(shí)現(xiàn)框架的搭建,利用模板來(lái)實(shí)現(xiàn)基本頁(yè)面的顯示(目前的數(shù)據(jù)還是靜態(tài)的)
這部分內(nèi)容中將不在顯示靜態(tài) API 頁(yè)面
新創(chuàng)建一個(gè)項(xiàng)目,將資源文件夾拷貝到項(xiàng)目的文件夾中去(拷貝方式見(jiàn)前一章節(jié)),文件夾中的內(nèi)容暫時(shí)不用考慮
資源文件夾介紹: dynamic 文件夾中用來(lái)存放處理動(dòng)態(tài)數(shù)據(jù)的文件,也就是我們的 WEBFrame 文件 static文件夾中有兩個(gè)文件夾,CSS和JS,CSS 文件夾存放對(duì)頁(yè)面樣式文件谴仙,JS 文件夾存放對(duì)頁(yè)面交互動(dòng)作的文件templates 文件夾中實(shí)現(xiàn)好了三個(gè)頁(yè)面的顯示模板,實(shí)現(xiàn)好了頁(yè)面的顯示基本框架,我們只需要利用框架,向框架里添加顯示數(shù)據(jù)即可
3.2.2 創(chuàng)建工程,導(dǎo)入資源模板文件
? 實(shí)現(xiàn)過(guò)程:
? 1. 打開(kāi)工程文件位置,將三個(gè)文件夾復(fù)制到工程文件夾中.將 WEBServer 和 WEBFrame 文件也復(fù)制進(jìn)去
? 2. 一般的情況下,會(huì)將數(shù)據(jù)處理的 WEBFrame 文件放到 dynamic 目錄下
? 實(shí)現(xiàn)代碼:
? ? ? 修改導(dǎo)入的相應(yīng)文件和調(diào)用 因?yàn)閿?shù)據(jù)文件被創(chuàng)建在 dynamic 這個(gè)文件夾下,所以在導(dǎo)入時(shí),需要form-import 方式導(dǎo)入模塊
? WebServer.py
from dynamic import WEBFrame
# 其它無(wú)修改
3.2.3 使用模板 Index 頁(yè)面實(shí)現(xiàn)首頁(yè)數(shù)據(jù)顯示
? 頁(yè)面在顯示時(shí),很多時(shí)候顯示框架都是一樣的,只是顯示的內(nèi)容不同
? ? ? 比如新聞頁(yè)面,都有標(biāo)題,時(shí)間,作者,內(nèi)容等等固定的顯示形式和位置
? 所以只需要將具體的新聞內(nèi)容加到寫(xiě)好的框架中顯示即可,這就是模板的作用
? 實(shí)現(xiàn)過(guò)程:
? 1. 服務(wù)文件到此基本上不會(huì)再做修改
? 2. 主要都是在數(shù)據(jù)處理文件上進(jìn)行處理
? 3. 因現(xiàn)目前的工程已經(jīng)不在訪問(wèn)API頁(yè)面,并且的所有的頁(yè)面布局文件都在static目錄下存放
? 4. 在數(shù)據(jù)處理文件中根據(jù)訪問(wèn)的路徑,拼接需要顯示的html模板文件,然后拼接文件地址 './templates/index.html'
? 5. 然后讀取文件中的內(nèi)容
? 6. 將文件中需要顯示內(nèi)容的地方,使用正則進(jìn)行替換 {\%content\%} 為內(nèi)容
? 7. 可以多加入一些數(shù)據(jù)
? 8. 準(zhǔn)備插入的數(shù)據(jù):
row_str = """
? ? ? ? <tr>
? ? ? ? ? ? <td>1</td>
? ? ? ? ? ? <td>000007</td>
? ? ? ? ? ? <td>全新好</td>
? ? ? ? ? ? <td>10.01%</td>
? ? ? ? ? ? <td>4.40%</td>
? ? ? ? ? ? <td>16.05</td>
? ? ? ? ? ? <td>14.60</td>
? ? ? ? ? ? <td>2017-07-18</td>
? ? ? ? ? ? <td>
? ? ? ? ? ? ? ? <input type="button" value="添加" id="toAdd" name="toAdd" systemidvaule="000007">
? ? ? ? ? ? </td>
? ? ? ? </tr>? """
將數(shù)據(jù)顯示到頁(yè)面中
? 實(shí)現(xiàn)代碼:
WebServer.py
# ...
# 前面的代碼不需要修改
# ------------- 這里開(kāi)始修改代碼------------
? ? try:
? ? ? ? #拼接路徑,因現(xiàn)目前的工程已經(jīng)不在訪問(wèn)API頁(yè)面,并且的所有的 html 頁(yè)面模板布局文件都在 static 目錄下存放
? ? ? ? #為了能正確的讀取頁(yè)面布局文件,所以要將原來(lái) ./html 修改為./static,程序會(huì)到 static 目錄下找訪問(wèn)的路徑對(duì)應(yīng)的模板文件進(jìn)行讀取
? ? ? ? f = open("./static" + file_name, "rb")
? ? except:
# 后面的代碼不需要修改
# ...
WebFrame.py
# 因?yàn)橐褂谜齽t替換模板文件中的內(nèi)容碾盐,所以先導(dǎo)入模塊
import re
# 實(shí)現(xiàn) WSGI 協(xié)議中的 application 接口方法
def application(environ, start_response):
? ? # 從服務(wù)器傳過(guò)來(lái)的字典中將訪問(wèn)路徑取出來(lái)
? ? url_path = environ['PATH_INFO']
? ? # 準(zhǔn)備一個(gè)用來(lái)保存讀取模板文件內(nèi)容的變量
? ? if url_path == '/index.py':
? ? ? ? path = './templates/index.html'
? ? ? ? # 讀取模板文件中的內(nèi)容
? ? ? ? with open(path, 'r') as f:
? ? ? ? ? ? file_content = f.read()
? ? ? ? # 替換的假數(shù)據(jù)(后面需要從數(shù)據(jù)庫(kù)里讀然味濉)
? ? ? ? row_str = """
? ? ? ? ? ? ? ? ? ? <tr>
? ? ? ? ? ? ? ? ? ? ? ? <td>1</td>
? ? ? ? ? ? ? ? ? ? ? ? <td>000007</td>
? ? ? ? ? ? ? ? ? ? ? ? <td>全新好</td>
? ? ? ? ? ? ? ? ? ? ? ? <td>10.01%</td>
? ? ? ? ? ? ? ? ? ? ? ? <td>4.40%</td>
? ? ? ? ? ? ? ? ? ? ? ? <td>16.05</td>
? ? ? ? ? ? ? ? ? ? ? ? <td>14.60</td>
? ? ? ? ? ? ? ? ? ? ? ? <td>2017-07-18</td>
? ? ? ? ? ? ? ? ? ? ? ? <td>
? ? ? ? ? ? ? ? ? ? ? ? ? ? <input type="button" value="添加" id="toAdd" name="toAdd" systemidvaule="000007">
? ? ? ? ? ? ? ? ? ? ? ? </td>
? ? ? ? ? ? ? ? ? ? </tr>? """
? ? ? ? # 準(zhǔn)備多條數(shù)據(jù)
? ? ? ? all_data = ""
? ? ? ? for i in range(20):
? ? ? ? ? ? all_data += row_str
? ? ? ? # 將行數(shù)據(jù),利用正則,將數(shù)據(jù)替換到模板中{%content%} 為了避免打錯(cuò),建議復(fù)制
? ? ? ? # 第一個(gè)參數(shù)為正則,用來(lái)匹配模板中的內(nèi)容
? ? ? ? # 參數(shù)二為要替換上去的內(nèi)容
? ? ? ? # 參數(shù)三是要被替換的字符串
? ? ? ? # 替換后重新更新數(shù)據(jù)并返回
? ? ? ? # 正則表達(dá)式中,因?yàn)閧}本身在正則中有限定符的作用毫玖,但是這里要替換的字符串包括{}掀虎,所以在{}前加上\,表示匹配{}
? ? ? ? file_content = re.sub(r'\{%content%\}', all_data, file_content)
? ? elif url_path == '/center.py':
? ? ? ? file_content = 'Center Page ...'
? ? else:
? ? ? ? file_content = 'Other Page ...'
? ? # 回調(diào) start_response 函數(shù)付枫,將響應(yīng)狀態(tài)信息回傳給服務(wù)器
? ? start_response('200 OK', [('Content-Type', 'text/html;charset=utf-8')])
? ? # 返回響應(yīng)數(shù)據(jù)內(nèi)容
? ? return file_content
利用模板烹玉,將制做的假數(shù)據(jù)顯示到頁(yè)面成功。
3.2.4 使用模板 Center 頁(yè)面實(shí)現(xiàn)首頁(yè)數(shù)據(jù)顯示
實(shí)現(xiàn)過(guò)程:
? 1. 個(gè)人中心頁(yè)面顯示同理首頁(yè)
? 2. 在數(shù)據(jù)處理文件中根據(jù)訪問(wèn)的路徑,拼接需要顯示的html模板文件,然后拼接文件地址 './templates/center.html'
? 3. 然后讀取文件中的內(nèi)容
? 4. 將文件中需要顯示內(nèi)容的地方,使用正則進(jìn)行替換 {\%content\%} 為內(nèi)容
? 5. 可以多加入一些數(shù)據(jù)
? 6. 準(zhǔn)備顯示的數(shù)據(jù):
row_str = """
? ? <tr>
? ? ? ? <td>000426</td>
? ? ? ? <td>興業(yè)礦業(yè)</td>
? ? ? ? <td>0.41%</td>
? ? ? ? <td>2.17%</td>
? ? ? ? <td>9.71</td>
? ? ? ? <td>9.67</td>
? ? ? ? <td>今天的漲幅不錯(cuò),希望每天如此</td>
? ? ? ? <td>
? ? ? ? ? ? <a type="button" class="btn btn-default btn-xs" href="/update/000426.html"> <span class="glyphicon glyphicon-star" aria-hidden="true"></span> 修改 </a>
? ? ? ? </td>
? ? ? ? <td>
? ? ? ? ? ? <input type="button" value="刪除" id="toDel" name="toDel" systemidvaule="000426">
? ? ? ? </td>
? ? </tr> """
將數(shù)據(jù)顯示到頁(yè)面中
? 實(shí)現(xiàn)代碼: WebFrame.py
? ? # ...
? ? # 前面的代碼不需要修改
? ? # ------------- 這里開(kāi)始修改代碼------------
? ? elif url_path == '/center.py':
? ? ? ? path = './templates/center.html'
? ? ? ? with open(path, 'r') as f:
? ? ? ? ? ? file_content = f.read()
? ? ? ? row_str = """
? ? ? ? ? ? ? ? <tr>
? ? ? ? ? ? ? ? ? ? <td>000426</td>
? ? ? ? ? ? ? ? ? ? <td>興業(yè)礦業(yè)</td>
? ? ? ? ? ? ? ? ? ? <td>0.41%</td>
? ? ? ? ? ? ? ? ? ? <td>2.17%</td>
? ? ? ? ? ? ? ? ? ? <td>9.71</td>
? ? ? ? ? ? ? ? ? ? <td>9.67</td>
? ? ? ? ? ? ? ? ? ? <td>今天的漲幅不錯(cuò),希望每天如此</td>
? ? ? ? ? ? ? ? ? ? <td>
? ? ? ? ? ? ? ? ? ? ? ? <a type="button" class="btn btn-default btn-xs" href="/update/000426.html"> <span class="glyphicon glyphicon-star" aria-hidden="true"></span> 修改 </a>
? ? ? ? ? ? ? ? ? ? </td>
? ? ? ? ? ? ? ? ? ? <td>
? ? ? ? ? ? ? ? ? ? ? ? <input type="button" value="刪除" id="toDel" name="toDel" systemidvaule="000426">
? ? ? ? ? ? ? ? ? ? </td>
? ? ? ? ? ? ? ? </tr> """
? ? ? ? all_data = ''
? ? ? ? for i in range(5):
? ? ? ? ? ? all_data += row_str
? ? ? ? file_content = re.sub(r'\{%content%\}', all_data, file_content)
? ? ? ? # 后面的代碼不需要修改
? ? ? ? # ...
3.2.5 代碼優(yōu)化阐滩,抽取函數(shù)
通過(guò)上面的代碼可以看出二打,如果頁(yè)面很多的話,那么我們就要不斷的的將功能實(shí)現(xiàn)代碼寫(xiě)入到 if-else 中,那么這個(gè)一個(gè)函數(shù)中的代碼就會(huì)非常大,非常不符合代碼開(kāi)發(fā)的規(guī)范。
好不容易從服務(wù)器文件中分離出來(lái)的代碼掂榔,在這里還是一坨继效,顯然是不合適的。那么怎么將這部分代碼再優(yōu)化一下呢装获?
可以將每個(gè)頁(yè)面的功能抽取成單獨(dú)的函數(shù),然后只需要在if中調(diào)用訪問(wèn)路徑相應(yīng)的函數(shù)執(zhí)行瑞信。如果再加入新功能,只需要再加入新方法就可以了穴豫。代碼管理非常清晰凡简。
? 實(shí)現(xiàn)過(guò)程:
? 1. 抽取方法
? 2. 調(diào)用函數(shù)
? 實(shí)現(xiàn)代碼: WebFrame.py
# 因?yàn)橐褂谜齽t替換模板文件中的內(nèi)容,所以先導(dǎo)入模塊
import re
# 實(shí)現(xiàn) WSGI 協(xié)議中的 application 接口方法
def application(environ, start_response):
? ? # 從服務(wù)器傳過(guò)來(lái)的字典中將訪問(wèn)路徑取出來(lái)
? ? url_path = environ['PATH_INFO']
? ? # 根據(jù)訪問(wèn)的頁(yè)面來(lái)調(diào)用相應(yīng)的方法
? ? if url_path == '/index.py':
? ? ? ? file_content = index()
? ? elif url_path == '/center.py':
? ? ? ? file_content = center()
? ? else:
? ? ? ? file_content = other()
? ? # 回調(diào) start_response 函數(shù),將響應(yīng)狀態(tài)信息回傳給服務(wù)器
? ? start_response('200 OK', [('Content-Type', 'text/html;charset=utf-8')])
? ? # 返回響應(yīng)數(shù)據(jù)內(nèi)容
? ? return file_content
#首頁(yè)響應(yīng)的函數(shù)
def index():
? ? path = './templates/index.html'
? ? # 讀取模板文件中的內(nèi)容
? ? with open(path, 'r') as f:
? ? ? ? file_content = f.read()
? ? # 替換的假數(shù)據(jù)(后面需要從數(shù)據(jù)庫(kù)里讀瘸由)
? ? row_str = """
? ? ? ? ? ? ? ? ? ? <tr>
? ? ? ? ? ? ? ? ? ? ? ? <td>1</td>
? ? ? ? ? ? ? ? ? ? ? ? <td>000007</td>
? ? ? ? ? ? ? ? ? ? ? ? <td>全新好</td>
? ? ? ? ? ? ? ? ? ? ? ? <td>10.01%</td>
? ? ? ? ? ? ? ? ? ? ? ? <td>4.40%</td>
? ? ? ? ? ? ? ? ? ? ? ? <td>16.05</td>
? ? ? ? ? ? ? ? ? ? ? ? <td>14.60</td>
? ? ? ? ? ? ? ? ? ? ? ? <td>2017-07-18</td>
? ? ? ? ? ? ? ? ? ? ? ? <td>
? ? ? ? ? ? ? ? ? ? ? ? ? ? <input type="button" value="添加" id="toAdd" name="toAdd" systemidvaule="000007">
? ? ? ? ? ? ? ? ? ? ? ? </td>
? ? ? ? ? ? ? ? ? ? </tr>? """
? ? # 準(zhǔn)備多條數(shù)據(jù)
? ? all_data = ""
? ? for i in range(20):
? ? ? ? all_data += row_str
? ? # 將行數(shù)據(jù),利用正則,將數(shù)據(jù)替換到模板中{%content%} 為了避免打錯(cuò),建議復(fù)制
? ? # 第一個(gè)參數(shù)為正則,用來(lái)匹配模板中的內(nèi)容
? ? # 參數(shù)二為要替換上去的內(nèi)容
? ? # 參數(shù)三是要被替換的字符串
? ? # 替換后重新更新數(shù)據(jù)并返回
? ? file_content = re.sub(r'\{%content%\}', all_data, file_content)
? ? return file_content
# 個(gè)人中心頁(yè)面響應(yīng)的函數(shù)
def center():
? ? path = './templates/center.html'
? ? with open(path, 'r') as f:
? ? ? ? file_content = f.read()
? ? row_str = """
? ? ? ? ? ? ? ? <tr>
? ? ? ? ? ? ? ? ? ? <td>000426</td>
? ? ? ? ? ? ? ? ? ? <td>興業(yè)礦業(yè)</td>
? ? ? ? ? ? ? ? ? ? <td>0.41%</td>
? ? ? ? ? ? ? ? ? ? <td>2.17%</td>
? ? ? ? ? ? ? ? ? ? <td>9.71</td>
? ? ? ? ? ? ? ? ? ? <td>9.67</td>
? ? ? ? ? ? ? ? ? ? <td>今天的漲幅不錯(cuò),希望每天如此</td>
? ? ? ? ? ? ? ? ? ? <td>
? ? ? ? ? ? ? ? ? ? ? ? <a type="button" class="btn btn-default btn-xs" href="/update/000426.html"> <span class="glyphicon glyphicon-star" aria-hidden="true"></span> 修改 </a>
? ? ? ? ? ? ? ? ? ? </td>
? ? ? ? ? ? ? ? ? ? <td>
? ? ? ? ? ? ? ? ? ? ? ? <input type="button" value="刪除" id="toDel" name="toDel" systemidvaule="000426">
? ? ? ? ? ? ? ? ? ? </td>
? ? ? ? ? ? ? ? </tr> """
? ? all_data = ''
? ? for i in range(5):
? ? ? ? all_data += row_str
? ? file_content = re.sub(r'\{%content%\}', all_data, file_content)
? ? return file_content
# 其它頁(yè)面響應(yīng)的函數(shù)
def other():
? ? return 'Other Page ...'
3.2.6 路由
? <1>什么是路由帜乞?
? ? ? 路由是指根據(jù)請(qǐng)求的地址,決定到目標(biāo)的路徑過(guò)程溉仑。這是一個(gè)計(jì)算機(jī)通用概念挖函,一般使用在網(wǎng)絡(luò)中。
? ? ? 在 Web框架中浊竟,路由是指客戶端把請(qǐng)求發(fā)送給 Web 服務(wù)器怨喘,Web 服務(wù)器再把請(qǐng)求發(fā)送給程序?qū)嵗3绦驅(qū)嵗枰缹?duì)每個(gè) URL 請(qǐng)求運(yùn)行哪些代碼振定,所以程序?qū)嵗4媪艘粋€(gè) URL 到 Python 函數(shù)的映射關(guān)系必怜。處理URL和函數(shù)之間關(guān)系的程序稱路由。
? 在上面的代碼中后频,if 函數(shù)就實(shí)現(xiàn)了簡(jiǎn)單的路由功能梳庆,if 函數(shù)通過(guò)訪問(wèn)路徑來(lái)決定調(diào)用哪個(gè)函數(shù)來(lái)執(zhí)行返回。上面的代碼雖然抽取了方法,實(shí)現(xiàn)了簡(jiǎn)單的路由功能卑惜,但是調(diào)用起來(lái)依然很繁瑣膏执,我們還是要在程序入加入很多的if-else,來(lái)實(shí)現(xiàn)函數(shù)的調(diào)用。那么有沒(méi)有辦法更簡(jiǎn)單的實(shí)現(xiàn)路由功能呢露久?
? 為了解決這個(gè)問(wèn)題,我們?cè)诖嘶A(chǔ)上,利用字典的鍵值對(duì)實(shí)現(xiàn)路由來(lái)調(diào)用函數(shù)
? 創(chuàng)建一個(gè)字典更米,使用字典保存 訪問(wèn)路徑 和 對(duì)應(yīng)方法 的鍵值對(duì)
? 通過(guò)在字典中查找訪問(wèn)的路徑來(lái)找到對(duì)應(yīng)的函數(shù)進(jìn)行調(diào)用實(shí)現(xiàn)路由。
? 實(shí)現(xiàn)過(guò)程:
? 1. 創(chuàng)建一個(gè)用來(lái)保存訪問(wèn)路徑和對(duì)應(yīng)函數(shù)的字典,并將鍵值對(duì)關(guān)系存入字典中
? 2. 當(dāng)訪問(wèn)頁(yè)面時(shí),通過(guò)訪問(wèn)路徑得到對(duì)應(yīng)的函數(shù),然后執(zhí)行
? 3. 如果以后有新的頁(yè)面添加,那么就向字典中加入新的鍵值對(duì)即可
? 實(shí)現(xiàn)代碼: WebFrame.py
def application(environ, start_response):
? ? # 從服務(wù)器傳過(guò)來(lái)的字典中將訪問(wèn)路徑取出來(lái)
? ? url_path = environ['PATH_INFO']
? ? # ------------- 這里開(kāi)始修改代碼------------
? ? # 創(chuàng)建一個(gè)路由字典毫痕,將訪問(wèn)路徑和響應(yīng)函數(shù)的鍵值對(duì)加入到字典中
? ? router_dict = {'/index.py': index, '/center.py': center}
? ? # 判斷訪問(wèn)的路徑在不在字典的key中
? ? if url_path in router_dict:
? ? ? ? # 如果在征峦,在字典中查找傳入的訪問(wèn)路徑,找到對(duì)應(yīng)的函數(shù)
? ? ? ? function = router_dict[url_path]
? ? else:
? ? ? ? # 如果不在消请,那么設(shè)置一個(gè)默認(rèn)函數(shù)栏笆,不然頁(yè)面無(wú)法顯示,不友好
? ? ? ? function = other
? ? # 執(zhí)行函數(shù)得到數(shù)據(jù)
? ? file_content = function()
? ? # ------------- 這里結(jié)束修改代碼------------
? ? # 回調(diào) start_response 函數(shù)臊泰,將響應(yīng)狀態(tài)信息回傳給服務(wù)器
? ? start_response('200 OK', [('Content-Type', 'text/html;charset=utf-8')])
? ? # 返回響應(yīng)數(shù)據(jù)內(nèi)容
? ? return file_content
通過(guò)字典來(lái)實(shí)現(xiàn)路由蛉加,使代碼更加簡(jiǎn)單。
3.2.7 擴(kuò)展裝飾器傳參
知識(shí)點(diǎn)擴(kuò)展:
裝飾器也可以傳參
# 定義帶參數(shù)的裝飾器
def set_args(args):
? ? print(args)
? ? def set_fun(func):
? ? ? ? print('set_func')
? ? ? ? def wrapper(*args,**kwargs):
? ? ? ? ? ? print('wrapper')
? ? ? ? ? ? return func(*args,**kwargs)
? ? ? ? return wrapper
? ? return set_fun
# 利用裝飾器來(lái)裝飾函數(shù)
@set_args('args-string')
def show():
? ? print('show')
# 調(diào)用函數(shù)
show()
程序執(zhí)行結(jié)果:
args-string
set_func
wrapper
show
裝飾器本來(lái)就是使用閉包來(lái)實(shí)現(xiàn)的缸逃,而閉包就是一種函數(shù)嵌套實(shí)現(xiàn)的形式七婴。
帶參的裝飾器就是在原有裝飾閉包函數(shù)外又套了一層函數(shù)
裝飾器在執(zhí)行裝飾過(guò)程中,先將裝飾器當(dāng)成一個(gè)函數(shù)來(lái)調(diào)用察滑,得到內(nèi)部函數(shù)的引用打厘,然后再將這個(gè)內(nèi)部函數(shù)做為真正的裝飾器來(lái)裝飾實(shí)際函數(shù)。
帶參裝飾器執(zhí)行過(guò)程:
? 1. 先執(zhí)行set_args('參數(shù)') 得到 set_args函數(shù)中的返回值,也就是set_fun函數(shù)的引用
? 2. 然后返回的引用和@進(jìn)行組合,變成裝飾器形式 @set_fun , 但是這時(shí)set_fun 函數(shù)因?yàn)槭欠祷氐拈]包引用,所以保留了args的參數(shù)值
? 3. 再調(diào)用 show 的時(shí)候,show 還是指向的 wrapper 函數(shù)贺辰,但是在這個(gè)函數(shù)中户盯,可以使用外面兩層函數(shù)的變量或參數(shù)
? 4. 無(wú)論在何時(shí)嵌施,閉包有幾層,最終被裝飾的函數(shù)永遠(yuǎn)指向 wrapper 函數(shù)
裝飾器有了傳參莽鸭,那么能不能利用這個(gè)特性吗伤,來(lái)改進(jìn)一下我們的代碼呢?
3.2.8 裝飾器實(shí)現(xiàn)路由
前面版本的代碼在實(shí)現(xiàn)函數(shù)的調(diào)用時(shí)硫眨,還是需要做大量的鍵值對(duì)添加工作
那么我們是不是可以利用裝飾器傳參來(lái)自動(dòng)進(jìn)行向字典中添加訪問(wèn)路徑和函數(shù)之間的關(guān)系呢?
如果可以,那么以后,我們只需要完成訪問(wèn)頁(yè)面的響應(yīng)函數(shù)就可以了足淆,這個(gè)也是必須要寫(xiě)的
然后在響應(yīng)函數(shù)前面加上裝飾器,讓裝飾器自動(dòng)將鍵值對(duì)關(guān)系添加到字典中礁阁。這樣的話就不需要再手動(dòng)添加鍵值對(duì)關(guān)系了巧号。
實(shí)現(xiàn)過(guò)程:
? 1. 創(chuàng)建一個(gè)用來(lái)保存鍵值對(duì)關(guān)系的空字典
? 2. 創(chuàng)建一個(gè)帶參的閉包裝飾器
? 3. 在閉包裝飾器中傳入訪問(wèn)的頁(yè)面路徑,然后利用路徑,將函數(shù)本身加入到字典中
? 4. 為每個(gè)實(shí)現(xiàn)的函數(shù)都加上這個(gè)裝飾器
? 5. 在application函數(shù)中姥闭,直接調(diào)用字典丹鸿,通過(guò)訪問(wèn)路徑來(lái)得到對(duì)應(yīng)的響應(yīng)函數(shù)調(diào)用即可
# ------------- 這里開(kāi)始修改代碼1------------
# 創(chuàng)建一個(gè)空字典
router_dict = {}
# ------------- 這里結(jié)束修改代碼1------------
def application(environ, start_response):
? ? # 從服務(wù)器傳過(guò)來(lái)的字典中將訪問(wèn)路徑取出來(lái)
? ? url_path = environ['PATH_INFO']
? ? # 創(chuàng)建一個(gè)路由字典,將訪問(wèn)路徑和響應(yīng)函數(shù)的鍵值對(duì)加入到字典中
? ? # router_dict = {'/index.py': index, '/center.py': center}
? ? # 通過(guò)在路由字典中查找傳入的訪問(wèn)路徑棚品,找到對(duì)應(yīng)的函數(shù)
? ? if url_path in router_dict:
? ? ? ? function = router_dict[url_path]
? ? else:
? ? ? ? function = other
? ? # 執(zhí)行函數(shù)得到數(shù)據(jù)
? ? file_content = function()
? ? # 回調(diào) start_response 函數(shù)靠欢,將響應(yīng)狀態(tài)信息回傳給服務(wù)器
? ? start_response('200 OK', [('Content-Type', 'text/html;charset=utf-8')])
? ? # 返回響應(yīng)數(shù)據(jù)內(nèi)容
? ? return file_content
# ------------- 這里開(kāi)始修改代碼2------------
# 實(shí)現(xiàn)一個(gè)帶參的裝飾器
def router(url_path):
? ? def set_func(func):
? ? ? ? def wrapper(*args,**kwargs):
? ? ? ? ? ? return func(*args,**kwargs)
? ? ? ? #將 wrapper 的引用存入字典入,以 url_path 參數(shù)做為 key,wrapper 指向就是被裝飾函數(shù)本身
? ? ? ? #一定要注意存入的書(shū)寫(xiě)位置,這條語(yǔ)句在執(zhí)行 @set_func 時(shí)執(zhí)行,將鍵值關(guān)系存入字典,否則就錯(cuò)了
? ? ? ? router_dict[url_path] = wrapper
? ? ? ? return wrapper
? ? return set_func
# ------------- 這里結(jié)束修改代碼2------------
# 為每個(gè)函數(shù)添加裝飾器
@router('/index.py')
def index():
? ? pass # 這里功能代碼不需要修改,筆記中省略
@router('/center.py')
def center():
? ? pass # 這里功能代碼不需要修改铜跑,筆記中省略
3.2.9 AOP面向切面編程概念介紹
? <1>什么是AOP面向切面編程:
? ? ? Aspect Oriented Programming AOP面向切面編程
? ? ? 他和面向?qū)ο缶幊桃粯用殴郑彩且环N編程思想。使用 AOP 編程思想锅纺,更利于程序的擴(kuò)展
? ? ? AOP是指在編程過(guò)程中薪缆,開(kāi)發(fā)人員并不關(guān)心這個(gè)程序如何開(kāi)始,如何結(jié)束伞广,只需要關(guān)心,如何添加一個(gè)功能的代碼實(shí)現(xiàn)
? <2>如何實(shí)現(xiàn)AOP編程:
? ? ? 在 python 中疼电,使用裝飾器來(lái)完成AOP編程
? 當(dāng)代碼添加后嚼锄,程序就可以自動(dòng)識(shí)別并運(yùn)行,原理類(lèi)似多米諾骨蔽豺,我們并不關(guān)心從哪開(kāi)始区丑,到哪結(jié)束,我只需要考慮在哪多加一片修陡,加上這片以后,也不影響骨牌的傳遞沧侥。上個(gè)版本的代碼中,使用裝飾器裝飾響應(yīng)函數(shù)魄鸦,基本上就實(shí)現(xiàn)了面向切面編程的思想宴杀。不需要知道程序如何執(zhí)行,只需要在實(shí)現(xiàn)頁(yè)面響應(yīng)功能函數(shù)前加上一個(gè)裝飾器即可拾因。程序在開(kāi)發(fā)過(guò)程中旺罢,并不需要考慮程序運(yùn)行的前因后果旷余,只需要將自己需求的功能實(shí)現(xiàn)在函數(shù)中即可,程序會(huì)自動(dòng)調(diào)用扁达,就像是一張骨牌
3.2.10 SEO 實(shí)現(xiàn)偽靜態(tài)服務(wù)器
? Search Engine Optimization 搜索引擎優(yōu)化:
? ? ? 搜索引擎優(yōu)化是一種利用搜索引擎的搜索規(guī)則來(lái)提高目前網(wǎng)站在有關(guān)搜索引擎內(nèi)的自然排名的方式正卧。
? SEO的目的:
? ? ? 為了從搜索引擎中獲得更多的免費(fèi)流量,從網(wǎng)站結(jié)構(gòu)跪解、內(nèi)容建設(shè)方案炉旷、用戶互動(dòng)傳播、頁(yè)面等角度進(jìn)行合理規(guī)劃叉讥,使網(wǎng)站更適合搜索引擎的索引原則的行為窘行;
? 在搜索時(shí),靜態(tài)頁(yè)面結(jié)果排名比動(dòng)態(tài)頁(yè)面靠前
? ? ? 但是實(shí)際效果不大节吮,顯示在前面的都是競(jìng)價(jià)排名的.花錢(qián)好辦事(比如X田系)
? 為了讓網(wǎng)頁(yè)地址支持SEO,將訪問(wèn)地址中的 xxx.py 改成 xxx.html 的形式
? 在實(shí)際的頁(yè)面訪問(wèn)時(shí),并不會(huì)有 .py 這種格式的文件抽高,所以為了支持SEO,將動(dòng)態(tài)頁(yè)面?zhèn)窝b成靜態(tài)頁(yè)面透绩,將訪問(wèn)地址改為 .html
? 實(shí)現(xiàn)過(guò)程:
? 1. 服務(wù)器文件中用來(lái)判斷文件類(lèi)型的位置要進(jìn)行修改
? 2. 訪問(wèn)地址修改后,數(shù)據(jù)處理文件中相應(yīng)的內(nèi)容也要進(jìn)行修改
? 3. 被裝飾的函數(shù)要修改
? 代碼實(shí)現(xiàn):
? ? ? WebServer.py
#判斷訪問(wèn)路徑的類(lèi)型
if file_name.endswith('.html'):
? WebFrame
# 因?yàn)樵L問(wèn)頁(yè)面變成了 .html 的后綴翘骂,所以裝飾器在傳參的時(shí)候,也需要傳入的是 .html
@set_args('/index.html')
def index():
? ? pass
@set_args('/center.html')
def center():
? ? pass
? 注意: 模板文件中有兩個(gè)用來(lái)實(shí)現(xiàn)頁(yè)面內(nèi)跳轉(zhuǎn)的文件連接帚豪,也是以 .py 文件名進(jìn)行調(diào)用的碳竟,也需要改成 .html
? ? ? index.html文件
? ? ? ? ? ■ <li><a href="/center.html">個(gè)人中心</a></li>
? ? ? center.html文件:
? ? ? ? ? ■ <li><a href="/index.html">個(gè)人中心</a></li>
3.2.11 小結(jié)
至此,我們已經(jīng)完成了MiniWeb框架的編寫(xiě)工作狸臣。
在編程過(guò)程中莹桅,要理解路由,模板等概念烛亦,并且理解使用它們的目的诈泼。