品牌建設(shè)和品牌推廣優(yōu)化網(wǎng)站打開速度
文章目錄
- Web應(yīng)用簡介
- 什么是Web框架?
- 什么是Web?
- 應(yīng)用程序的兩種模式
- Web應(yīng)用程序的優(yōu)缺點
- 手寫Web框架
- HTTP協(xié)議的相關(guān)知識
- 1.四大特性
- 2.請求數(shù)據(jù)格式
- 3.響應(yīng)數(shù)據(jù)格式
- 手寫框架
- 使用wsgiref模塊
- 基于wsgiref模塊搭建Web框架(最初版)
- 基于wsgiref模塊搭建Web框架(第二版)
- 基于wsgiref模塊搭建Web框架(最終版)
- Python主流Web框架
- 1.Django:重量級框架
- 2.Flask:輕量級框架
- 3.Tornado:異步非阻塞框架
- 4.fastapi框架
前言:
我們之前學(xué)習(xí)了數(shù)據(jù)庫、前端、Python基礎(chǔ)等三大部分,但是他們?nèi)龎K的內(nèi)容沒有串在一起,也就沒辦法開發(fā)出一個完成的web項目出來,因此,我們通過Django框架把這三者融合在一起,以后我們就可以很方便的開發(fā)出各種各樣的項目。
在了解Django之前,我們可以先了解web框架,了解它的好處是什么呢?在一步步搭建它的過程中,我們會逐步明白到Django是如何寫的,以及如果處理頁面接收與反饋給頁面數(shù)據(jù)的。當(dāng)然 這樣講可能有點抽象,那么我們先來了解一下吧!
Web應(yīng)用簡介
什么是Web框架?
Web框架是用來進行Web應(yīng)用開發(fā)的一個軟件架構(gòu),主要用于動態(tài)網(wǎng)絡(luò)開發(fā)。開發(fā)者在基于Web框架實現(xiàn)自己的業(yè)務(wù)邏輯。Web框架實現(xiàn)了很多功能,為實現(xiàn)業(yè)務(wù)邏輯提供了一套通用方法。
框架的意思就是別人提前寫好的框架(就是一堆目錄和文件),我們只需要按照人家的要求在固定的位置寫代碼即可
什么是Web?
Web應(yīng)用程序時一種可以通過Web訪問的應(yīng)用程序,用戶只需要有瀏覽器即可,無需再安裝其他軟件
應(yīng)用程序的兩種模式
應(yīng)用程序有兩種模式C/S、B/S兩種模式
C/S模式是客戶端----->服務(wù)端程序,也就是說這類程序一般獨立運行。
B/S模式是瀏覽器端----->服務(wù)端應(yīng)用程序,這類應(yīng)用程序一般借助IE等瀏覽器來運行。
而Web應(yīng)用程序一般是B/S模式
Web應(yīng)用程序的優(yōu)缺點
優(yōu)點:
- 只需要一個適用的瀏覽器即可,無需安裝其他應(yīng)用軟件
- 節(jié)省用戶的硬盤空間資源
- 它們無需更新,因為所有新的特性都在服務(wù)端上執(zhí)行,從而自動傳達到客戶端
- 跨平臺使用。例如:Windows、Mac、Linux等。
缺點:
- 嚴(yán)重依賴服務(wù)端的正常運行,一旦服務(wù)端出現(xiàn)問題、宕機,會直接影響客戶端正常訪問
手寫Web框架
Web應(yīng)用程序時B/S架構(gòu)的,所以我們需要自己寫一個服務(wù)端,這里的Web服務(wù)端是我們使用socket套接字來實現(xiàn)的,以瀏覽器作為客戶端,朝我們搭建的服務(wù)端發(fā)送數(shù)據(jù),已經(jīng)我們的服務(wù)端給瀏覽器返回數(shù)據(jù)的過程。
因為這里是用瀏覽器做客戶端,就涉及到了HTTP協(xié)議的相關(guān)知識
HTTP協(xié)議的相關(guān)知識
1.四大特性
- 基于請求和響應(yīng)
- 基于TCP協(xié)議之上的應(yīng)用層協(xié)議
- 無狀態(tài)
- 短連接
2.請求數(shù)據(jù)格式
- 請求首行(請求方式、協(xié)議、版本號、路徑)
- 請求頭
- 換行(\r\n\r\n)
- 請求體(Get請求方式是沒有請求體的,Post請求方式才有請求體)
3.響應(yīng)數(shù)據(jù)格式
- 響應(yīng)首行(響應(yīng)狀態(tài)碼)
- 響應(yīng)頭
- 換行(\r\n\r\n)
- 響應(yīng)體(一般情況下就是瀏覽器要展示給用戶看的數(shù)據(jù))
具體HTTP協(xié)議的相關(guān)知識可以去看我這篇博客里面的“HTTP超文本傳輸協(xié)議節(jié)點處即可”
HTTP協(xié)議相關(guān)知識
手寫框架
'''手寫框架'''
import socket # 導(dǎo)入socket套接字模塊# 生成一個socket對象,并設(shè)置協(xié)議和類型(括號內(nèi)不寫默認是基于TCP協(xié)議)
server = socket.socket(family=socket.AF_INET,type=socket.SOCK_STREAM)
'我這里是補全的TCP協(xié)議的,也可以選擇UDP'# 綁定ip和端口號給服務(wù)端
server.bind(('127.0.0.1', 8000))# 建立監(jiān)聽 半連接池
server.listen(5)while True:# 等待客戶端的連接sock, addr = server.accept()# 接收客戶端發(fā)送的請求數(shù)據(jù)data = sock.recv(1024) # 單次最大字節(jié)數(shù) 接收的是bytes類型# print(data.decode('utf-8')) # 解碼接收的請求數(shù)據(jù)'''當(dāng)我們在瀏覽器輸入127.0.0.1:8000/index時通過打印請求數(shù)據(jù)我們看到了請求方式(Get 朝服務(wù)端索要數(shù)據(jù) Post 朝服務(wù)端提交數(shù)據(jù))GET /index HTTP/1.1Host: 127.0.0.1:8000這樣我們可以通過切分字符串并以索引的方式獲取到想要的數(shù)據(jù)'''# 從請求數(shù)據(jù)中看到可以直接按照空格切分字符串str_data = data.decode('utf-8').split(' ')[1]# print(str_data)# 回應(yīng)客戶端請求數(shù)據(jù)'''因為是瀏覽器客戶端所以需要遵循HTTP協(xié)議的需求'''# TCP流式傳輸協(xié)議,短時間內(nèi)可以分多次接收多個數(shù)據(jù)sock.send(b'HTTP/1.1 200 ok \r\n\r\n')'''然后我們需要根據(jù)網(wǎng)址后綴的不同請求不同的內(nèi)容如:127.0.0.1:8000/index等所以我們得回到接收的請求數(shù)據(jù)位置處篩選出我們想要的數(shù)據(jù)''''''然后通過判斷來進行根據(jù)后綴回應(yīng)不同的數(shù)據(jù)'''if str_data == '/index':sock.send(b'from index')elif str_data == '/home':sock.send(b'from home')elif str_data == '/html':with open(r'../Myhtml.html', 'rb') as f: # rb模式 二進制sock.send(f.read())else:sock.send(b'404 Error')
但是這種Web框架的代碼缺陷有點多
- socket代碼重復(fù)編寫
- 針對請求數(shù)據(jù)格式的處理復(fù)雜且重復(fù)
- 針對不同網(wǎng)址后綴的匹配邏輯過于簡單
- 沒有解決高并發(fā)問題
那么該怎么優(yōu)化以上存在的問題呢?
使用wsgiref模塊
基于wsgiref模塊搭建Web框架(最初版)
wsgiref模塊是內(nèi)置模塊,很多Web框架底層使用的模塊
來看看使用wsgiref模塊怎么做吧
from wsgiref.simple_server import make_server # 導(dǎo)入模塊def run(request,respose):""":param request: 客戶端的請求數(shù)據(jù):param respose: 給客戶端進行響應(yīng)的數(shù)據(jù):return: 返回給客戶端的真實數(shù)據(jù)"""respose('200 ok', []) # 返回響應(yīng)狀態(tài)給客戶端print(request) # 這個參數(shù)是wsgiref幫助我們封裝的一個大字典,字典中攜帶的客戶端的請求數(shù)據(jù)return [b'hello world!'] # 返回給客戶單的真實數(shù)據(jù)if __name__ == '__main__':'''我們可以通過查看源碼的方式看到make_server中需要的幾個參數(shù)host, port, app, server_class=WSGIServer, handler_class=WSGIRequestHandler'''server = make_server('127.0.0.1', 8000, app=run) # 分別代表:ip地址、端口號、由那個程序來處理請求
'''
1.它會實時監(jiān)聽127.0.0.1:8000這個地址,只要由客戶端來連接這個地址,它就可以做出響應(yīng)
2.app=run:它的意思是,當(dāng)由客戶端請求過來的時候,會把該請求交給run函數(shù)來處理,但是這個地方不要加括號
3.django中這里寫的是函數(shù)名,當(dāng)請求來的時候,會調(diào)用這個函數(shù),函數(shù)叫括號
4.如果是flask框架,當(dāng)請求來的時候,也會把請求交給這個對象處理,只不過變成了對象()---實例化----調(diào)用__call__方法執(zhí)行
(__call__方法里面寫的就是flask框架的源碼入口位置)
'''server.serve_forever() # 將服務(wù)器持續(xù)開啟
而用戶請求的數(shù)據(jù):打印出來的是一個字典的形式的
基于wsgiref模塊搭建Web框架(第二版)
那么此時,我們可以在瀏覽器發(fā)送請求時,稍作變動
比如:讓用戶訪問127.0.0.1:8000/index,那么頁面要出現(xiàn)index后綴我們該怎么實現(xiàn)。
首先服務(wù)端要獲取url后面的/index,才能進行處理。
from wsgiref.simple_server import make_server # 導(dǎo)入模塊def run(request,respose):""":param request: 客戶端的請求數(shù)據(jù):param respose: 給客戶端進行響應(yīng)的數(shù)據(jù):return: 返回給客戶端的真實數(shù)據(jù)"""respose('200 ok', []) # 返回響應(yīng)狀態(tài)給客戶端print(request) # 這個參數(shù)是wsgiref幫助我們封裝的一個大字典,字典中攜帶的客戶端的請求數(shù)據(jù)# 獲取大字典中的ip和端口號后面的后綴current_path = request.get('PATH_INFO')'進行判斷請求數(shù)據(jù)的后綴回應(yīng)不同的數(shù)據(jù)'if current_path == '/index':return [b'from index']if current_path == '/home':with open(r'Myhtml.html','rb')as f:return f.read()else:return [b'404 Error']# return [b'hello world!'] # 返回給客戶單的真實數(shù)據(jù)if __name__ == '__main__':server = make_server('127.0.0.1', 8000, app=run) # 分別代表:ip地址、端口號、由那個程序來處理請求server.serve_forever() # 將服務(wù)器持續(xù)開啟
從這里就已經(jīng)實現(xiàn)了當(dāng)時使用基于TCP協(xié)議的socket套接字的服務(wù)端的功能
,并且優(yōu)化掉了代碼重復(fù)寫、請求數(shù)據(jù)格式的處理的缺陷。但是還沒有解決針對不同網(wǎng)址后綴的匹配邏輯過于簡單問題。并且按照上面的思路是不是一個頁面堆滿了if語句,所以我們需要將用戶訪問、已經(jīng)處理響應(yīng)用戶的步驟進行拆分。那么我們可以通過代碼的封裝優(yōu)化。
基于wsgiref模塊搭建Web框架(最終版)
代碼的封裝優(yōu)化
解決問題:1.網(wǎng)址后綴的匹配問題2.每個后綴匹配成功后執(zhí)行的代碼有多有少面條版 函數(shù)版 模塊版3.將分支的代碼封裝成一個個函數(shù)4.將網(wǎng)址后綴與函數(shù)名做對應(yīng)關(guān)系5.獲取網(wǎng)址后綴循環(huán)匹配6.如果想新增功能只需要先寫函數(shù)在 添加一個對應(yīng)關(guān)系即可7.根據(jù)不同的功能拆分不同的py文件views.py 存儲核心業(yè)務(wù)邏輯(功能函數(shù))urls.py 存儲網(wǎng)址后綴與函數(shù)名對應(yīng)關(guān)系templates目錄 存儲html頁面文件(模塊文件)start.py/run.py 啟動文件、入口文件8.為了使函數(shù)體代碼中業(yè)務(wù)邏輯有更多的數(shù)據(jù)可用將request大字典轉(zhuǎn)手傳給這個函數(shù)(可用不用但是不能沒有)
先展示封裝代碼在一個頁面中
from wsgiref.simple_server import make_server # 導(dǎo)入模塊def index():return 'from index'def home():return 'from home''定義一個元組用來存儲想要書寫的后綴對應(yīng)的頁面'
urls = (('/index', index),('/home', home)
)def run(request,respose):""":param request: 客戶端的請求數(shù)據(jù):param respose: 給客戶端進行響應(yīng)的數(shù)據(jù):return: 返回給客戶端的真實數(shù)據(jù)"""respose('200 ok', []) # 返回響應(yīng)狀態(tài)給客戶端print(request) # 這個參數(shù)是wsgiref幫助我們封裝的一個大字典,字典中攜帶的客戶端的請求數(shù)據(jù)# 獲取大字典中的ip和端口號后面的后綴current_path = request.get('PATH_INFO')# '''進行判斷請求數(shù)據(jù)的后綴回應(yīng)不同的數(shù)據(jù)'''# if current_path == '/index':# # return [b'from index']# res = index()# return [res.encode('utf-8')]# elif current_path == '/home':# with open(r'Myhtml.html','rb')as f:# return f.read()# else:# return [b'404 Error']'''循環(huán)這個元組來判斷是否存在后綴'''func = None # 定義一個空變量來存放for url in urls: # ('/index', index),if current_path == url[0]:func = url[1] # 后綴對應(yīng)的函數(shù)名break # 一旦匹配到了內(nèi)容就立刻結(jié)束for循環(huán)'''判斷當(dāng)后綴存在時執(zhí)行'''if func:res = func()return [res.encode('utf-8')]else:return [b'404 Error']if __name__ == '__main__':server = make_server('127.0.0.1', 8000, app=run) # 分別代表:ip地址、端口號、由那個程序來處理請求server.serve_forever() # 將服務(wù)器持續(xù)開啟
到此發(fā)現(xiàn)如果后續(xù)需要添加更多的功能,還是會有很多重復(fù),所以我們需要根據(jù)py文件中的功能不同劃分不同的py文件
文件拆分
我們需要先建立一個文件夾,存放三個文件以及一個文件夾(用于存放模版文件html)
urls:用于表示用戶輸入的url應(yīng)哪一個函數(shù)
'''根據(jù)用戶輸入的url,返回該url對應(yīng)的處理函數(shù)'''from views import *urls = (('/index', index),('/home', home),('/demo', demo))
views:用于處理用戶的請求與響應(yīng)用戶數(shù)據(jù)
'''接收用戶請求、在對應(yīng)函數(shù)可以進行一番處理然后響應(yīng)給用戶數(shù)據(jù)。'''def index():return 'from index'def home():return 'from home'def demo():with open(r'templates/dj.html', 'r', encoding='utf-8') as f:return f.read()
我們的Web框架的入口程序
from wsgiref.simple_server import make_server
from urls import urlsdef run(request,response):""":param request: 客戶端的請求數(shù)據(jù):param respose: 給客戶端進行響應(yīng)的數(shù)據(jù):return: 返回給客戶端的真實數(shù)據(jù)"""response('200 ok',[]) # 返回響應(yīng)狀態(tài)給客戶端# 獲取大字典中的ip和端口號后面的后綴current_path = request.get('PATH_INFO')func = None # 定義一個空變量,用于接受處理用戶請求的函數(shù)返回的數(shù)據(jù)'''循環(huán)這個元組來判斷是否存在后綴'''for url in urls:# 判斷用戶輸入的頁面后綴地址,是否在我們定義的處理列表中if current_path == url[0]:func = url[1] # 后綴對應(yīng)的函數(shù)名break # 一旦匹配到了內(nèi)容就立刻結(jié)束for循環(huán)'''判斷當(dāng)后綴存在時執(zhí)行'''if func:res = func()return [res.encode('utf-8')]else:return [b'404 Error'] # 當(dāng)上述判斷不執(zhí)行時,執(zhí)行else條件,返回404 errorif __name__ == '__main__':server = make_server('127.0.0.1', 8000, run)server.serve_forever() # 服務(wù)器持續(xù)開啟
還有最后一個是templates文件夾存放模版文件,也就是html文件
Python主流Web框架
1.Django:重量級框架
Django是基于Python的免費和開放源代碼Web框架
,它遵循模型-模版-視圖(MTV)體系結(jié)構(gòu)模式。它封裝的功能非常豐富并且非常多,所以它是重量級框架,Django的文檔最完善、是市面最主流框架、市場占有率最高。
但如果需要實現(xiàn)的功能很少的項目就會顯得略微笨重。
2.Flask:輕量級框架
Flask是Python編寫的一種輕量級(微)的Web開發(fā)框架
,只提供Web框架的核心功能,較其他類型的框架更為的自由、靈活,更加適合高度定制化的Web項目。
Flask在功能上面沒有欠缺,Flask的第三方開源組件比較豐富,因此Flask對開發(fā)人員的水平有了一定的要求。使用Flask開發(fā)較為依賴于第三方組件
3.Tornado:異步非阻塞框架
Tornado是一種Web服務(wù)器軟件的開源版本。
Tornado和主流Web服務(wù)器框架(包括大多數(shù)Python的框架)有著明顯的區(qū)別:它是非阻塞式的服務(wù)器,而且速度相當(dāng)快。得利于其非阻塞的方式和對epoll的運用,Tornado每秒可以處理數(shù)以千計的連接,因此Tornado是實時Web服務(wù)的一個理想框架。但很多功能都需要開發(fā)者自己去編寫,因此學(xué)習(xí)這個框架成本有點高。
4.fastapi框架
它主要用來寫一些接口,制作不出來頁面,它只負責(zé)寫業(yè)務(wù)邏輯