如何搭建一个简易的Web框架
Web框架本質(zhì)
什么是Web框架, 如何自己搭建一個(gè)簡(jiǎn)易的Web框架?其實(shí), 只要了解了HTTP協(xié)議, 這些問(wèn)題將引刃而解.
簡(jiǎn)單的理解:? 所有的Web應(yīng)用本質(zhì)上就是一個(gè)socket服務(wù)端, 而用戶的瀏覽器就是一個(gè)socket客戶端.
用戶在瀏覽器的地址欄輸入網(wǎng)址, 敲下回車(chē)鍵便會(huì)給服務(wù)端發(fā)送數(shù)據(jù), 這個(gè)數(shù)據(jù)是要遵守統(tǒng)一的規(guī)則(格式)的, 這個(gè)規(guī)則便是HTTP協(xié)議. HTTP協(xié)議主要規(guī)定了客戶端和服務(wù)器之間的通信格式
瀏覽器收到的服務(wù)器響應(yīng)的相關(guān)信息可以在瀏覽器調(diào)試窗口(F12鍵開(kāi)啟)的Network標(biāo)簽頁(yè)中查看, 點(diǎn)擊view source即可以查看原始響應(yīng)數(shù)據(jù)(有些網(wǎng)頁(yè)可能并沒(méi)有該項(xiàng))
訪問(wèn)碼云網(wǎng)站的原始響應(yīng)數(shù)據(jù)(節(jié)選)
HTTP/1.1 200 OK
Date: Thu, 16 May 2019 13:30:59 GMT
Content-Type: text/html; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
每個(gè)HTTP請(qǐng)求和響應(yīng)都遵循相同的格式, 一個(gè)HTTP包含Header和Body兩部分, 其中Body是可選的. HTTP響應(yīng)的Header中有一個(gè)響應(yīng)的內(nèi)容格式. 如text/html表示HTML網(wǎng)頁(yè)
? HTTP GET請(qǐng)求的格式??
? HTTP 響應(yīng)的格式??
以上內(nèi)容總結(jié)為一句話便是:?要使自己寫(xiě)的Web server端正常運(yùn)行起來(lái), 必須要使我們自己的Web server端在給客戶端回復(fù)消息時(shí)按照HTTP協(xié)議的規(guī)則加上響應(yīng)狀態(tài)行
自定義Web框架
一 響應(yīng)指定內(nèi)容的Web框架
瀏覽器訪問(wèn)127.0.0.1:9001將返回Hello World標(biāo)題字樣
import socket # 導(dǎo)入socket模塊def main():# 實(shí)例化socket對(duì)象sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# 綁定IP地址與端口sock.bind(('127.0.0.1', 9001))# 監(jiān)聽(tīng) sock.listen()while True:conn, addr = sock.accept()data = conn.recv(1024)str = data.decode("UTF-8").strip(" ")print("瀏覽器請(qǐng)求信息>>>", str)# 如果瀏覽器請(qǐng)求信息非空則進(jìn)行回復(fù)if str:# 給回復(fù)的消息加上響應(yīng)狀態(tài)行conn.send(b"HTTP/1.1 200 OK\r\n\r\n")conn.send(b"<h1>Hello World</h1>")conn.close()# 否則跳過(guò)本次循環(huán), 開(kāi)始下一次循環(huán)else:continueif __name__ == "__main__":main()二 響應(yīng)HTML文件的Web框架
? (1)?首先創(chuàng)建一個(gè)html文件??
??一個(gè)展示標(biāo)題與當(dāng)前時(shí)間的網(wǎng)頁(yè), 命名為index.html
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><style>#in1{width: 400px;height: 60px;font-size: 26px;font-weight: bloder;line-height: 30px;border: none;}</style><title>index</title> </head> <body><h1>歡迎訪問(wèn)簡(jiǎn)易版Web框架主頁(yè)</h1><input type="text" id="in1"/><script>var item;function f(){var time = new Date(); // 實(shí)例化時(shí)間對(duì)象var year = time.getFullYear(); // 獲得年var month = time.getMonth() + 1; // 獲得月 var date = time.getDate(); // 獲得日var hours = time.getHours(); // 獲得小時(shí) var minutes = time.getMinutes(); // 獲得分鐘var seconds = time.getSeconds(); // 獲得秒 // 月份與日期的顯示為兩位數(shù)字如01月01日if(month < 10 ){month = "0" + month;}if(date < 10 ){date = "0" + date;}// 時(shí)間拼接var dateTime = year + "年" + month + "月" + date + "日" + hours + "時(shí)" + minutes + "分" + seconds + "秒";// 利用ID獲取到input元素var inputEle = document.getElementById("in1");// 將input元素的值設(shè)置為當(dāng)前時(shí)間 inputEle.value = dateTime;}// 定義啟動(dòng)函數(shù)function start(){// 初始化當(dāng)前時(shí)間 f();// 利用定時(shí)器每隔一段時(shí)間執(zhí)行獲取當(dāng)前時(shí)間與賦值函數(shù)f item = setInterval(f, 1000);}// 調(diào)用啟動(dòng)函數(shù) start()</script> </body> </html>在該html文件中可添加img標(biāo)簽, 其src屬性值如果是網(wǎng)絡(luò)地址也是可以直接在瀏覽器上現(xiàn)實(shí)的
在該html文件中的css樣式與js操作同樣可以直接在瀏覽器上顯示出來(lái)
? (2)?準(zhǔn)備服務(wù)端程序, 文件命名為server.py??
import socket # 導(dǎo)入socket模塊 import os # 導(dǎo)入os模塊def main():# 利用os模塊拼接路徑html_path = os.path.join(os.path.dirname(__file__), "index.html")# 實(shí)例化socket對(duì)象sk = socket.socket()# 綁定IP地址與端口sk.bind(('127.0.0.1',9001))# 監(jiān)聽(tīng) sk.listen()# 計(jì)數(shù)i = 1 while True:# 等待瀏覽器連接獲取連接conn, _ = sk.accept()# 接收瀏覽器請(qǐng)求data = conn.recv(1024)# 將瀏覽器請(qǐng)求轉(zhuǎn)換為字符串并格式化str = data.decode('utf-8').strip(" ")# 打印瀏覽器響應(yīng)print('瀏覽器請(qǐng)求信息>>>:', str, i)# 計(jì)數(shù)自加i += 1 # 如果瀏覽器請(qǐng)求內(nèi)容并不為空, 響應(yīng)瀏覽器請(qǐng)求if str:# 為響應(yīng)的數(shù)據(jù)加上相應(yīng)狀態(tài)行conn.send(b'HTTP/1.1 200 ok \r\n\r\n')# 以bytes數(shù)據(jù)類(lèi)型打開(kāi)html文件with open(html_path,'rb') as f:# 讀取數(shù)據(jù)data = f.read()# 發(fā)送html文件數(shù)據(jù) conn.send(data)# 關(guān)閉與瀏覽器的連接 conn.close()# 若瀏覽器請(qǐng)求信息為空則關(guān)閉連接并跳過(guò)本次循環(huán), 開(kāi)始下一次循環(huán)else:conn.close()continueif __name__ == "__main__":main()注意: 該例子使用相對(duì)路徑, index.html與server.py需在同一目錄下
三 根據(jù)瀏覽器請(qǐng)求響應(yīng)數(shù)據(jù)的Web框架
以上簡(jiǎn)易的框架基本上都是指定了要給瀏覽器返回什么數(shù)據(jù), 這樣肯定滿足不了我們的需求, 那么如何才能根據(jù)瀏覽器的請(qǐng)求, 響應(yīng)相對(duì)應(yīng)的數(shù)據(jù)呢?
CSS, JS, 圖片等文件都叫做網(wǎng)站的靜態(tài)文件
??(1) 為了測(cè)試, 首先創(chuàng)建一個(gè)html文件, 命名為index.html??
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>index</title><!-- 引入外部CSS文件 --><link rel="stylesheet" href="css.css"><!-- 引入外部JS文件 --><script src="js.js"></script> </head> <body><h1>歡迎訪問(wèn)Web框架首頁(yè)</h1><!-- 綁定事件 --><div onmouseover="mOver(this)"; onmouseout="mOut(this)">把鼠標(biāo)移到上面</div> </body> </html>? ??(2) 接著創(chuàng)建一個(gè)CSS文件, 命名為css.css??
div {/* 初始化元素背景色為綠色 */background-color:green;/* 初始化元素寬200px */width:200px;/* 初始化元素高200px */height:200px;/* 初始化元素內(nèi)填充40px */padding:40px;/* 初始化字體顏色為白色 */color:#ffffff; }?? (3)?再創(chuàng)建一個(gè)JS文件, 命名為js.js??
// 定義鼠標(biāo)覆蓋事件觸發(fā)函數(shù) function mOver(obj) {// 文字替換為"謝謝"obj.innerHTML="謝謝"// 背景顏色更改為紅obj.style.backgroundColor= "red"; } // 定義鼠標(biāo)非覆蓋狀態(tài)事件觸發(fā)函數(shù) function mOut(obj) { // 文字替換為"把鼠標(biāo)以到上面"obj.innerHTML="把鼠標(biāo)移到上面"// 背景顏色更改為綠obj.style.backgroundColor= "green"; }? ???(4)?準(zhǔn)備服務(wù)端程序, 文件命名為server.py??
import os # 導(dǎo)入os模塊 import socket # 導(dǎo)入socket模塊 # 導(dǎo)入線程模塊 from threading import Thread # 實(shí)例化socket對(duì)象 server = socket.socket() # 綁定IP及端口 server.bind(("127.0.0.1", 9001)) server.listen()# 路徑拼接 html_path = os.path.join(os.path.dirname(__file__), "index.html") css_path = os.path.join(os.path.dirname(__file__), "css.css") js_path = os.path.join(os.path.dirname(__file__), "js.js")def html(conn):"""響應(yīng)"/"請(qǐng)求"""conn.send(b'HTTP/1.1 200 ok \r\n\r\n')with open(html_path, mode="rb") as f:content = f.read()conn.send(content)conn.close()def css(conn):"""響應(yīng)"/css.css"請(qǐng)求"""conn.send(b"HTTP/1.1 200 ok \r\n\r\n")with open(css_path, mode="rb") as f:content = f.read()conn.send(content)conn.close()def js(conn):"""響應(yīng)"/js.js"請(qǐng)求"""conn.send(b"HTTP/1.1 200 ok \r\n\r\n")with open(js_path, mode="rb") as f:content = f.read()conn.send(content)conn.close()def NotFound(conn):conn.send(b"HTTP/1.1 200 ok \r\n\r\n")conn.send(b"<h1>404NotFound!</h1>")# 請(qǐng)求列表 request_list = [("/", html),("/css.css", css),("/js.js", js) ]def get(conn):"""處理響應(yīng)函數(shù)"""try: # 異常處理req = conn.recv(1024).decode("UTF-8")req = req.split("\r\n")[0].split()[1]# 打印瀏覽器請(qǐng)求print(req)except IndexError:pass# 遍歷請(qǐng)求列表進(jìn)行響應(yīng)for request in request_list:# 若瀏覽器請(qǐng)求信息等于請(qǐng)求列表中的項(xiàng),則進(jìn)行響應(yīng)# 判斷服務(wù)端是否能夠進(jìn)行響應(yīng)if req == request[0]:# 獲取線程對(duì)象, 實(shí)現(xiàn)并發(fā)t = Thread(target=request[1], args=(conn, ))# 啟動(dòng)線程 t.start()# 響應(yīng)后結(jié)束遍歷breakelse: # 若本次循環(huán)未匹配則跳過(guò)本次循環(huán)開(kāi)始下一次continueelse: # 若所有請(qǐng)求皆不匹配則調(diào)用NotFound函數(shù), 表示無(wú)法響應(yīng) NotFound(conn)def main():while True:# 利用線程實(shí)現(xiàn)并發(fā)# 獲取TCP連接conn, _ = server.accept()t = Thread(target=get, args=(conn,))t.start()if __name__ == "__main__":main()? 注意: 該例子使用相對(duì)路徑, index.html, css.css, js.js與server.py需在同一目錄下
四 進(jìn)階版Web框架
以上的幾版Web框架比較基礎(chǔ), 一些定義的函數(shù)使用起來(lái)也比較繁瑣, 可定制性很差, 修改起來(lái)也比較困難.?
利用Python提供的一些模塊可以簡(jiǎn)化一些步驟, 并且使框架的可定制性更好, 可以方便其他人進(jìn)行定制使用
? 結(jié)構(gòu)示意圖??
?
??文件結(jié)構(gòu)??
構(gòu)建Web框架
??(1) 構(gòu)建目錄??
新建文件夾frame
1)?文件夾內(nèi)創(chuàng)建__init__.py文件(內(nèi)容為空)
2) 文件夾內(nèi)新建文件夾file
? (2) 準(zhǔn)備html文件??
index.html文件
<!DOCTYPE html> <html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><style>/* 時(shí)間展示樣式 */#in1{width: 400px;height: 60px;font-size: 26px;font-weight: bloder;line-height: 30px;border: none;}</style><title>index</title></head><body><!-- 標(biāo)題 --><h1>歡迎訪問(wèn)簡(jiǎn)易版Web框架主頁(yè)</h1><!-- 動(dòng)態(tài)替換(模板渲染), 刷新頁(yè)面動(dòng)態(tài)刷新 --><h2>@</h2><input type="text" id="in1"/><!-- 認(rèn)證表單 --><form action="http://127.0.0.1:9001/auth/" method="post"><label for="username">用戶名</label><input type="text" id="username" name="username"/><label for="password">密碼</label><input type="password" id="password" name="password"/><input type="submit"></form><script>var item;function f(){var time = new Date();var year = time.getFullYear();var month = time.getMonth() + 1;var date = time.getDate();var hours = time.getHours();var minutes = time.getMinutes();var seconds = time.getSeconds();// 月份與日期的顯示為兩位數(shù)字如01月01日if(month < 10 ){month = "0" + month;}if(date < 10 ){date = "0" + date;}// 時(shí)間拼接var dateTime = year + "年" + month + "月" + date + "日" + hours + "時(shí)" + minutes + "分" + seconds + "秒";var inputEle = document.getElementById("in1");inputEle.value = dateTime;}function start(){f();item = setInterval(f, 1000);}start()</script></body> </html>success.html文件
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>success</title> </head> <body><h1>登陸成功</h1> </body> </html>? 將以上兩個(gè)html文件保存到file文件夾內(nèi)
? (3) models.py文件??
首先需要?jiǎng)?chuàng)建一個(gè)數(shù)據(jù)庫(kù), 這里使用MySQL
-- 登錄MySQL mysql -u用戶名 -p密碼-- 查看數(shù)據(jù)庫(kù) SHOW DATABASES;-- 創(chuàng)建數(shù)據(jù)庫(kù) CREATE DATABASE 庫(kù)名;/*這里創(chuàng)建一個(gè)名為dbf的數(shù)據(jù)庫(kù)CREATE DATABASE dbf; */利用pymysql模塊操作數(shù)據(jù)庫(kù), 建表插入數(shù)據(jù)
models.py文件
import pymysql # 導(dǎo)入pymysql模塊, 需要下載 # pip install pymysqldef main():conn = pymysql.connect(host = "127.0.0.1", # mysql主機(jī)地址port = 3306, # mysql端口user = "root", # mysql遠(yuǎn)程連接用戶名password = "123", # mysql遠(yuǎn)程連接密碼database = "dbf", # mysql使用的數(shù)據(jù)庫(kù)名charset = "UTF8" # mysql使用的字符編碼,默認(rèn)為utf8 )# 實(shí)例化游標(biāo)對(duì)象cursor = conn.cursor(pymysql.cursors.DictCursor)# 創(chuàng)建表格sql1 = """CREATE TABLE userinfo(id int PRIMARY KEY auto_increment,username char(12) NOT NULL UNIQUE,password char(20) NOT NULL);"""# 向創(chuàng)建的表格中插入數(shù)據(jù)sql2 = """INSERT INTO userinfo(username, password) VALUES("a", "1"),("b", "2");"""# 將sql指令提交到緩存 cursor.execute(sql1)cursor.execute(sql2)# 提交并執(zhí)行sql指令 conn.commit()# 關(guān)閉游標(biāo) cursor.close()# 關(guān)閉與數(shù)據(jù)庫(kù)的連接 conn.close()if __name__ == "__main__":main()? (4) auth.py文件??
用于驗(yàn)證用戶登錄信息
auth.py文件
import pymysql # 導(dǎo)入pymysql模塊def auth(username, password):conn = pymysql.connect(host = "127.0.0.1", # mysql主機(jī)地址port = 3306, # mysql端口user = "root", # mysql遠(yuǎn)程連接用戶名password = "123", # mysql遠(yuǎn)程連接密碼database = "dbf", # mysql使用的數(shù)據(jù)庫(kù)名charset = "UTF8" # mysql使用的字符編碼,默認(rèn)為utf8 )# 打印用戶信息: 用戶名, 密碼print("userinfo", username, password)# 實(shí)例化游標(biāo)對(duì)象cursor = conn.cursor(pymysql.cursors.DictCursor)# sql查詢(xún)指令sql = "SELECT * FROM userinfo WHERE username=%s AND password=%s"# res獲取影響行數(shù)res = cursor.execute(sql, [username, password])if res: # 數(shù)據(jù)庫(kù)中存在該數(shù)據(jù), 返回Truereturn Trueelse: # 數(shù)據(jù)庫(kù)中不存在該數(shù)據(jù), 返回Falsereturn False? (5) views.py文件??
用于處理數(shù)據(jù)
views.py文件
"""該模塊存放瀏覽器請(qǐng)求對(duì)應(yīng)的網(wǎng)頁(yè)與urls模塊中url_list列表中的項(xiàng)存在映射關(guān)系若要添加新的內(nèi)容, 只需要定義相應(yīng)的函數(shù), 并將函數(shù)名以字符串的形式加入到__all__列表中 """import os # 導(dǎo)入os模塊 import time # 導(dǎo)入time模塊 import auth # 導(dǎo)入auth.py from urllib.parse import parse_qs # 導(dǎo)入parse_qs用于解析數(shù)據(jù)# 展示所有可用方法 __all__ = ["index","authed"# "css" ]# 路徑拼接(針對(duì)windows"/", linu需要把"/"改為"\") index_path = os.path.join( os.path.dirname(__file__), "file/index.html") success_path = os.path.join( os.path.dirname(__file__), "file/success.html")def index(environ):with open(index_path, mode="rb") as f:data = f.read().decode("UTF-8")# 將特殊符號(hào)@替換為當(dāng)前時(shí)間, 實(shí)現(xiàn)動(dòng)態(tài)網(wǎng)站data = data.replace("@", time.strftime(("%Y-%m-%d %H:%M:%S")))return data.encode("UTF-8")def authed(environ):if environ.get("REQUEST_METHOD") == "POST":try:request_body_size = int(environ.get("CONTENT_LENGTH", 0))except (ValueError):request_body_size = 0request_data = environ["wsgi.input"].read(request_body_size)print(">>>", request_data) # bytes數(shù)據(jù)類(lèi)型print("????", environ["QUERY_STRING"]) # "空的" - post請(qǐng)求只能按照以上方式獲取數(shù)據(jù)# parse_qs負(fù)責(zé)解析數(shù)據(jù)# 不管是POST還是GET請(qǐng)求都不能直接拿到數(shù)據(jù), 拿到的數(shù)據(jù)仍需要進(jìn)行分解提取# 所以引入urllib模塊中的parse_qs方法request_data = parse_qs(request_data.decode("UTF-8"))print("拆解后的數(shù)據(jù)", request_data) # {"username": ["a"], "password": ["1"]}username = request_data["username"][0]password = request_data["password"][0]status = auth.auth(username, password)if status:with open(success_path, mode="rb") as f:data = f.read()else:# 如果直接返回中文, 沒(méi)有給瀏覽器指定編碼格式, 默認(rèn)是gbk, 需要進(jìn)行g(shù)bk編碼, 使瀏覽器能夠識(shí)別# 這里已經(jīng)指定了編碼# start_response("200 OK", [("Content-Type", "text/html;charset=UTF8")])data = "<h1>用戶名或密碼錯(cuò)誤, 登陸失敗</h1>".encode("UTF-8")return dataif environ.get("REQUEST_METHOD") == "GET":print("????", environ["QUERY_STRING"]) # "username='a'&password='1'"字符出數(shù)據(jù)類(lèi)型 request_data = environ["QUERY_STRING"]# parse_qs負(fù)責(zé)解析數(shù)據(jù)# 不管是POST還是GET請(qǐng)求都不能直接拿到數(shù)據(jù), 拿到的數(shù)據(jù)仍需要進(jìn)行分解提取# 所以引入urllib模塊中的parse_qs方法request_data = parse_qs(request_data)print("拆解后的數(shù)據(jù)", request_data) # {"username": ["a"], "password": ["1"]}username = request_data["username"][0]password = request_data["password"][0]print(username, password)status = auth.auth(username, password)if status:with open(success_path, mode="rb") as f:data = f.read()else:# 如果直接返回中文, 沒(méi)有給瀏覽器指定編碼格式, 默認(rèn)使gbk, 需要進(jìn)行g(shù)bk編碼, 是瀏覽器能夠識(shí)別# 這里已經(jīng)指定了編碼# start_response("200 OK", [("Content-Type", "text/html;charset=UTF8")])data = "<h1>用戶名或密碼錯(cuò)誤, 登陸失敗</h1>".encode("UTF-8")return data# def css(environ): # with open("css.css", mode="rb") as f: # data = f.read() # return data? (6) urls.py文件??
映射表
urls.py文件
from views import index, authed """可在此處按照類(lèi)似格式添加任意內(nèi)容例如再向url_list列表中添加一項(xiàng), 按照如下格式("/css.css", css), 只需要再在views.py文件中創(chuàng)建一個(gè)對(duì)應(yīng)的函數(shù)即可 """ url_list = [("/", index),("/auth/", authed)# ("/css.css", css) ]? (7) manage.py文件??
? 主邏輯
manage.py文件
from urls import url_list from wsgiref.simple_server import make_serverdef application(environ, start_response):""":param environ: 包含所有請(qǐng)求信息的字典:param start_response: 封裝響應(yīng)信息(相應(yīng)行與響應(yīng)頭):return: [響應(yīng)主體]"""# 封裝響應(yīng)信息start_response("200 OK", [("Content-Type", "text/html;charset=UTF8")])# 打印包含所有請(qǐng)求信息的字典print(environ)# 打印請(qǐng)求路徑信息print(environ["PATH_INFO"])path = environ["PATH_INFO"]for p in url_list:if path == p[0]:data = p[1](environ)breakelse:continueelse:data = b"<h1>Sorry 404!, NOT Found The Page</h1>"# 返回響應(yīng)主體# 必須遵守此格式[內(nèi)容]return [data]if __name__ == "__main__":# 綁定服務(wù)器IP地址與端口號(hào), 調(diào)用函數(shù)frame = make_server("127.0.0.1", 9001, application)# 開(kāi)始監(jiān)聽(tīng)HTTP請(qǐng)求frame.serve_forever()??至此一個(gè)簡(jiǎn)易的Web框架就搭建好了, 我再來(lái)簡(jiǎn)單介紹一下啟動(dòng)步驟??
啟動(dòng)步驟
(1) 首先按照步驟,?執(zhí)行(3) models.py文件
1) 創(chuàng)建數(shù)據(jù)庫(kù)
2) 執(zhí)行models.py
(2) 執(zhí)行manage.py啟動(dòng)服務(wù)器
(3) 根據(jù)指定IP及端口, 使用瀏覽器訪問(wèn)
這里指定127.0.0.1:9001
效果演示
index頁(yè)面
登錄成功
登錄失敗
錯(cuò)誤請(qǐng)求
包/模塊解析
以上的框架中用到了兩個(gè)比較重要的包/模塊: wsgiref模塊與urllib包, 下面介紹一下
wsgiref模塊
WSGI簡(jiǎn)介引用
WSGI(Web?Server?Gateway?Interface)是一種規(guī)范, 它定義了使用Python編寫(xiě)的web應(yīng)用程序與web服務(wù)器程序之間的接口格式, 實(shí)現(xiàn)web應(yīng)用程序與web服務(wù)器程序間的解耦
常用的WSGI服務(wù)器有uwsgi、Gunicorn. 而Python標(biāo)準(zhǔn)庫(kù)提供的獨(dú)立WSGI服務(wù)器叫做wsgiref, Django開(kāi)發(fā)環(huán)境用的就是這個(gè)模塊來(lái)做服務(wù)器
wsgire模塊簡(jiǎn)介引用
wsgiref模塊其實(shí)就是將整個(gè)請(qǐng)求信息給封裝了起來(lái), 比如它將所有請(qǐng)求信息封裝成了一個(gè)叫做request的對(duì)象, 那么直接利用request.path就能獲取到本次請(qǐng)求的路徑. request.method就能獲取到本次請(qǐng)求的請(qǐng)求方式(GET/POST)等
?urllib包
urllib簡(jiǎn)介《Python參考手冊(cè)(第4版)》
urllib包提供了一個(gè)高級(jí)接口, 用于編寫(xiě)需要與HTTP服務(wù)器、FTP服務(wù)器和本地文件交互的客戶端. 典型的應(yīng)用程序包括從網(wǎng)頁(yè)抓取數(shù)據(jù)、自動(dòng)化、代理、Web爬蟲(chóng)等. 這是可配置程度最高的庫(kù)模塊之一
由于urllib包中功能模塊眾多且功能強(qiáng)大, 在此不做過(guò)多介紹, 僅介紹本框架所用模塊
在views.py中我們通過(guò)?from urllib.parse import parse_qs?導(dǎo)入了urllib包下的parser模塊中的parse_qs方法
parse模塊《Python參考手冊(cè)(第4版)》
urllib.parser模塊用于操作URL字符串, 如"http://www.python.org"
其中parse_qs方法:
parse_qs(qs [, keep_blank_values [, strict_parsing]])
解析URL編碼的(MIME類(lèi)型為application/x-www-form-urlencoded)查詢(xún)字符串qs, 并返回字典, 其中鍵是查詢(xún)變量名稱(chēng), 值是為每個(gè)名稱(chēng)定義的值列表. keep_blank_values是一個(gè)布爾值標(biāo)志,控制如何處理空白值. 如果為T(mén)rue, 則它們包含在字典中, 值設(shè)置為空字符串; 如果為False(默認(rèn)值), 則將其丟棄。strict_parsing是一個(gè)布爾值標(biāo)志, 如果為T(mén)rue, 則將解析錯(cuò)誤轉(zhuǎn)換為ValueError異常. 默認(rèn)情況下會(huì)忽略錯(cuò)誤
?
以上就是本人在學(xué)習(xí)Django框架前的學(xué)習(xí)總結(jié), 可供學(xué)習(xí)參考
?
轉(zhuǎn)載于:https://www.cnblogs.com/dmcs95/p/10886462.html
總結(jié)
以上是生活随笔為你收集整理的如何搭建一个简易的Web框架的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 背景图片随着浏览器拖动而变化
- 下一篇: LeetCode 52.N-Queens