本篇在上篇的基礎上爲其增設路由功能,同時將上篇中的數據庫中數據備份添加進去。php
在以前有介紹過爲一個函數不改變源代碼不改變原函數的調用方式下爲其增設附加功能,須要用到裝飾器,而在該上篇的web-mini框架中每當服務器發送動態資源請求過來時,咱們須要作if判斷,那麼咱們可不能夠省去這繁瑣的步驟呢?css
1.1 經過閉包來實現裝飾器:html
# 爲函數附加新功能 - 計算運算時長 import time def timmer(func): def wrapper(*args,**kwargs): start_time = time.time() ret = func(*args,**kwargs) end_time = time.time() print("spend time --> %s"%(end_time -start_time)) return ret return wrapper @timmer # 等價於 ==》test = timmer(test) def test(num): time.sleep(1) print("in the test --> %s"%num ) return num*num +1 ret = test(8) print(ret)
注1:裝飾器在原函數在調用以前就以及開始裝飾了。即寫上@timmer時test1 = timmer(test)就開始執行。下面咱們介紹一種由類實現的裝飾器:python
1.2 由類實現的裝飾器mysql
class Test(object): """定義一個裝飾器類""" def __init__(self,func): self.func = func def __call__(self,*args,**kwargs): print("在此爲函數附加新功能") ret = self.func(*args,**kwargs) return ret @Test # get_str = Test(get_str),至關於將get_str傳入建立實例對象 def get_str(num): print("in the get_str-->%s"%num) return "hello world!" get_str() # Test(get_str)() ,必須有call魔法方法,實則運行實例的__call__方法
這種由類實現的裝飾器,其效果與由閉包實現的裝飾器效果幾乎同樣,可是因爲進行一次裝飾,需建立一個實例對象,即每次須要開闢一個內存空間,存放着實例屬性、方法以及類的中方法的指針等,比較浪費資源,即 殺雞用牛刀;web
1.3 帶參數的裝飾器sql
def set_level(level_num): # 用來接收參數 def set_func(func): # 裝飾器函數 def call_func(*args,**kwargs): if level_num = 1: print("設置權限1") elif level_num = 2: print("設置權限2") else: print("你個瓜皮,沒有這個權限驗證") ret = func(*args,**kwargs) return ret return call_func return set_func @setlevel(1) def test1(): print("hello world") @setlevel(2) def test1(): print("媽的個巴子喲") # setlevel(para) --- test1 = set_level(1) # 一、首先調用set_level,而且傳入參數1, --- test1 = set_level(1) # 二、啓動裝飾器set_func,裝飾函數,--- test1 = set_func(test1)
帶參數的裝飾器:數據庫
一、其最外層函數set_level,至關於一個容器用來封裝,存儲裝飾器和一些變量等;而真正的裝飾器部分實則爲set_func裝飾器;瀏覽器
二、進行裝飾時:@setlevel(1)實則執行兩步操做:服務器
①、首先調用set_level,而且傳入參數1, --- test1 = set_level(1) ;
②、啓動裝飾器set_func,裝飾函數,--- test1 = set_func(test1);
目前開發的網站其實真正意義上都是動態網站,只是URL上有些區別,通常URL分爲靜態URL、動態URL、僞靜態URL,他們的區別是什麼?
靜態URL 靜態URL相似 域名/news/2012-5-18/110.html 咱們通常稱爲真靜態URL ,每一個網頁有真實的物理路徑,也就是真實存在服務器裏的。 |
動態URL 動態URL相似 域名/NewsMore.asp?id=5 或者 域名/DaiKuan.php?id=17,帶有?號的URL,咱們通常稱爲動態網址,每一個URL只是一個邏輯地址,並非真實物理存在服務器硬盤裏的。 |
僞靜態URL 僞靜態URL相似 域名/course/74.html 這個URL和真靜態URL相似。他是經過僞靜態規則把動態URL假裝成靜態網址。也是邏輯地址,不存在物理地址。 |
三者的優缺點:
一、靜態URL:網頁打開速度快,SEO最好,可是對於大中型網站而言頁面多,修改起來不方便,不便管理;
二、動態URL:因爲須要調用框架從數據庫中讀取數據,故網頁打開速度不如靜態URL,SEO不如靜態URL,可是適合中大型網站,修改頁面很方便,由於是邏輯地址,因此佔用硬盤空間要比純靜態網站小。
三、僞靜態URL:其輸入瀏覽器的形式與靜態URL相同,可是確實調用框架讀取數據庫中的數據來實現的,是相對於前兩種的折中方案;URL比較友好,利於記憶;修改頁面也十分方便,可是設置麻煩,服務器要支持重寫規則,小企業網站或者玩很差的就不要折騰;
該實例是在上篇(web-mini框架的基本實現(一))的基礎上對其進行修改的,即爲其添加路由功能,實現僞靜態網頁、以及替換成數據庫中的數據;
服務端web_server.py
import socket import re import multiprocessing import time # import dynamic.mini_frame import sys class WSGIServer(object): def __init__(self, port, app, static_path): # 1. 建立套接字 self.tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 2. 綁定 self.tcp_server_socket.bind(("", port)) # 3. 變爲監聽套接字 self.tcp_server_socket.listen(128) self.application = app self.static_path = static_path def service_client(self, new_socket): """爲這個客戶端返回數據""" # 1. 接收瀏覽器發送過來的請求 ,即http請求 # GET / HTTP/1.1 # ..... request = new_socket.recv(1024).decode("utf-8") # print(">>>"*50) # print(request) request_lines = request.splitlines() print("") print(">"*20) print(request_lines) # GET /index.html HTTP/1.1 # get post put del file_name = "" ret = re.match(r"[^/]+(/[^ ]*)", request_lines[0]) if ret: file_name = ret.group(1) # print("*"*50, file_name) if file_name == "/": file_name = "/index.html" # 2. 返回http格式的數據,給瀏覽器 # 2.1 若是請求的資源不是以.py結尾,那麼就認爲是靜態資源(html/css/js/png,jpg等) if not file_name.endswith(".html"): try: f = open(self.static_path + file_name, "rb") except: response = "HTTP/1.1 404 NOT FOUND\r\n" response += "\r\n" response += "------file not found-----" new_socket.send(response.encode("utf-8")) else: html_content = f.read() f.close() # 2.1 準備發送給瀏覽器的數據---header response = "HTTP/1.1 200 OK\r\n" response += "\r\n" # 2.2 準備發送給瀏覽器的數據---boy # response += "hahahhah" # 將response header發送給瀏覽器 new_socket.send(response.encode("utf-8")) # 將response ic.mini_frame.applicationbody發送給瀏覽器 new_socket.send(html_content) else: # 2.2 若是是以.py結尾,那麼就認爲是動態資源的請求 env = dict() # 這個字典中存放的是web服務器要傳遞給 web框架的數據信息 env['PATH_INFO'] = file_name # {"PATH_INFO": "/index.py"} # body = dynamic.mini_frame.application(env, self.set_response_header) body = self.application(env, self.set_response_header) header = "HTTP/1.1 %s\r\n" % self.status for temp in self.headers: header += "%s:%s\r\n" % (temp[0], temp[1]) header += "\r\n" response = header+body # 發送response給瀏覽器 new_socket.send(response.encode("utf-8")) # 關閉套接 new_socket.close() def set_response_header(self, status, headers): self.status = status self.headers = [("server", "mini_web v8.8")] self.headers += headers def run_forever(self): """用來完成總體的控制""" while True: # 4. 等待新客戶端的連接 new_socket, client_addr = self.tcp_server_socket.accept() # 5. 爲這個客戶端服務 p = multiprocessing.Process(target=self.service_client, args=(new_socket,)) p.start() new_socket.close() # 關閉監聽套接字 self.tcp_server_socket.close() def main(): """控制總體,建立一個web 服務器對象,而後調用這個對象的run_forever方法運行""" if len(sys.argv) == 3: try: port = int(sys.argv[1]) # 7890 frame_app_name = sys.argv[2] # mini_frame:application except Exception as ret: print("端口輸入錯誤。。。。。ret:",ret) return else: print("請按照如下方式運行:") print("python3 xxxx.py 7890 mini_frame:application") return # mini_frame:application ret = re.match(r"([^:]+):(.*)", frame_app_name) if ret: frame_name = ret.group(1) # mini_frame app_name = ret.group(2) # application else: print("請按照如下方式運行:") print("python3 xxxx.py 7890 mini_frame:application") return with open("./web_server.conf") as f: conf_info = eval(f.read()) # eval(str)函數很強大,官方解釋爲:將字符串str當成有效的表達式來求值並返回計算結果 # 此時 conf_info是一個字典裏面的數據爲: # { # "static_path":"./static", # "dynamic_path":"./dynamic" # } sys.path.append(conf_info['dynamic_path']) # import frame_name --->找frame_name.py frame = __import__(frame_name) # 返回值標記這 導入的這個模板 app = getattr(frame, app_name) # 此時app就指向了 dynamic/mini_frame模塊中的application這個函數 # print(app) wsgi_server = WSGIServer(port, app, conf_info['static_path']) wsgi_server.run_forever() if __name__ == "__main__": main()
基於上篇的web_server.py實現了僞靜態URL,即讀取的URL中的頁面filename是以.html結尾即視爲動態資源,則調用框架讀取數據庫中資源替換模板。至關於欺騙瀏覽器,輸入的是靜態URL,確實按照動態URL的方式進行處理的;
web_mini框架 mini_frame.py
import re from pymysql import connect # 方式二:經過定義裝飾器,自動添加文件名和函數名的映射關係,實現路由效果 URL_FUNC_DICT = dict() # 帶參數的裝飾器 def route(url): def get_func(func): URL_FUNC_DICT[url] = func def call_func(*args,**kwargs): ret = func() return ret return call_func return get_func def select_date(sql): conn = connect(host="localhost",port=3306,database="stock_db",user="root",password= "mysql",charset="utf8") cs = conn.cursor() cs.execute(sql) data_content = cs.fetchall() # 獲得的是一個元組,裏面有不少個元組 cs.close() conn.close() return data_content # 1.index = route("/index.py")即 index = get_func # 2.index = get_func(index)即index = call_func @route("/index.html") def index(): with open("./templates/index.html") as f: content = f.read() sql = "select * from info;" my_stock_info = select_date(sql) html_template = """ <tr> <td>{0}</td> <td>{1}</td> <td>{2}</td> <td>{3}</td> <td>{4}</td> <td>{5}</td> <td>{6}</td> <td>{7}</td> <td> <input type="button" value="添加" id="toAdd" name="toAdd" > </td> </tr> """ html = "" for temp in my_stock_info: html += html_template.format(*temp) # print("----->>>{}<<<---------".format(html)) content = re.sub(r"\{%content%\}", html, content) return content @route("/center.html") def center(): with open("./templates/center.html") as f: content = f.read() sql ="select i.code,i.short,i.chg,i.turnover,i.price,i.highs,f.note_info from info as i inner join focus as f on i.id=f.info_id;" my_stock_info = select_date(sql) # print(mys) html ="" html_template = """ <tr> <td>{0}</td> <td>{1}</td> <td>{2}</td> <td>{3}</td> <td>{4}</td> <td>{5}</td> <td>{6}</td> <td> <a type="button" class="btn btn-default btn-xs" href="/update/index.html"> <span class="glyphicon glyphicon-star" aria-hidden="true"></span> 修改 </a> </td> <td> <input type="button" value="刪除" id="toDel" name="toDel" > </td> </tr> """ for temp in my_stock_info: html += html_template.format(*temp) content = re.sub(r"\{%content%\}", html, content) return content # 方式一:這種方式經過映射,手動定義字典添加key爲文件名:value爲對應函數名; # URL_FUNC_DICT = { # "/index.py":index, # "/center.py":center # } print(URL_FUNC_DICT) def application(env, start_response): start_response('200 OK', [('Content-Type', 'text/html;charset=utf-8')]) file_name = env['PATH_INFO'] # file_name = "/index.py" run_func = URL_FUNC_DICT[file_name] return run_func() # if file_name == "/index.py": # return index() # elif file_name == "/center.py": # return center() # else: # return 'Hello World! 我愛你中國....'
基於上篇的 mini_frame.py 實現了添加路由功能、及替換成MySQL數據庫中的資源的效果;
over~~~其餘部分基本與其餘相同~~~