WEB框架本質
Python的WEB框架分為兩類:
1、自己寫socket,自己處理請求
2、基於wsgi(Web Server Gateway Interface WEB服務網關接口),自己處理請求
眾所周知,對於所有的Web應用,本質上其實就是一個socket服務端,用戶的瀏覽器其實就是一個socket客戶端。
看下面的代碼是WEB最本質的WEB框架(自己寫的socket,自己處理的請求)
#!/usr/bin/env python #coding:utf-8 import socket def handle_request(client): #接收請求 buf = client.recv(1024) #返回信息 client.send("HTTP/1.1 200 OK\r\n\r\n") client.send("Hello, Tim") def main(): #創建sock對象 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #監聽80端口 sock.bind(('localhost',8000)) #最大允許排隊的客戶端 sock.listen(5) #循環 while True: #等待用戶的連接,默認accept阻塞當有請求的時候往下執行 connection, address = sock.accept() #把連接交給handle_request函數 handle_request(connection) #關閉連接 connection.close() if __name__ == '__main__': main()
通過上面的代碼可以看出wsgi其實做的就是(寫了個socket)
在上面兩類在python中對應的框架:
1、自己寫socket,自己處理請求 == Tornado(還有就是Tronado有兩種模式,可以通過修改配置讓它使用自己寫的socket也可以基於wsgi)
2、其他的WEB框架,比如Django都是基於wsgi
這兩類的區別是一個是自己寫socket,在就是基於wsgi。
上述通過socket來實現了其本質,而對於真實開發中的python web程序來說,一般會分為兩部分:服務器程序和應用程序。服務器程序負責對socket服務器進行封裝,並在請求到來時,對請求的各種數據進行整理。應用程序則負責具體的邏輯處理。為了方便應用程序的開發,就出現了眾多的Web框架,例如:Django、Flask、web.py 等。不同的框架有不同的開發方式,但是無論如何,開發出的應用程序都要和服務器程序配合,才能為用戶提供服務。這樣,服務器程序就需要為不同的框架提供不同的支持。這樣混亂的局面無論對於服務器還是框架,都是不好的。對服務器來說,需要支持各種不同框架,對框架來說,只有支持它的服務器才能被開發出的應用使用。這時候,標准化就變得尤為重要。我們可以設立一個標准,只要服務器程序支持這個標准,框架也支持這個標准,那么他們就可以配合使用。一旦標准確定,雙方各自實現。這樣,服務器可以支持更多支持標准的框架,框架也可以使用更多支持標准的服務器。
WSGI(Web Server Gateway Interface)是一種規范,它定義了使用python編寫的web app與web server之間接口格式,實現web app與web server間的解耦。
python標准庫提供的獨立WSGI服務器稱為wsgiref。
#!/usr/bin/env python #coding:utf-8 from wsgiref.simple_server import make_server def RunServer(environ, start_response): start_response('200 OK', [('Content-Type', 'text/html')]) return '<h1>Hello, web!</h1>' if __name__ == '__main__': httpd = make_server('', 8000, RunServer) print "Serving HTTP on port 8000..." httpd.serve_forever() #接收請求 #預處理請求(封裝了很多http請求的東西)
當請求過來后就執行RunServer這個函數。
OK 下面看WEB框架圖(Socket & 處理請求的函數)

Socket接收請求並封裝請求,然后丟給處理請求的函數,處理請求的函數處理完成之后再給Socket然后返回給用戶!
OK 通過上面的圖可以更好的理解:
python中的WEB框架分為:即寫Socket又寫(處理請求)的WEB框架比如Tornado,還有就是不寫Socket然后自己寫處理請求的WEB框架:Django
自定義框架
1、通過python標准庫提供的wsgiref模塊開發一個自己的Web框架
先看下面的框架
#!/usr/bin/env python #coding:utf-8 from wsgiref.simple_server import make_server def RunServer(environ, start_response): start_response('200 OK', [('Content-Type', 'text/html')]) return '<h1>Hello, Web Frame!</h1>' if __name__ == '__main__': httpd = make_server('', 8000, RunServer) print "Serving HTTP on port 8000..." httpd.serve_forever()
當我們訪問的時候,訪問任何的url都會顯示,“Hello, Web Frame!”其他網站不是這樣的,看下面的操作。

斷點查看

這里面給咱們這么多大額請求的數據、端口號、方式、地址、請求的URL等。。。
那我們就可以通過這些信息獲取用戶請求的URL
#!/usr/bin/env python #coding:utf-8 from wsgiref.simple_server import make_server def RunServer(environ, start_response): start_response('200 OK', [('Content-Type', 'text/html')]) #根據url的不同,返回不同的字符串 #1 獲取URL[URL從哪里獲取?當請求過來之后執行RunServer,wsgi給咱們封裝了這些請求,這些請求都封裝到了,environ & start_response] request_url = environ['PATH_INFO'] print request_url #2 根據URL做不同的相應 #print environ #這里可以通過斷點來查看它都封裝了什么數據 if request_url == '/home/index': return "Hello Home" elif request_url == '/shuaige': return "Hello TianShuai" else: return '<h1>404!</h1>' if __name__ == '__main__': httpd = make_server('', 8000, RunServer) print "Serving HTTP on port 8000..." httpd.serve_forever()
看上面的操作,如果其他人使用你的框架,當用戶來訪問的時候,如果URL少還好,如果很多的話是不是URL寫起來非常麻煩,看下面的代碼
#!/usr/bin/env python #-*- coding:utf-8 -*- from wsgiref.simple_server import make_server '''可以進行拆分---這一部分可以給框架使用者,按照定義的格式來操作''' def index(): return 'index' def login(): return 'login' #1 定義一個列表,上面定義函數 url_list = [ #這里吧URL和函數做一個對應 ('/index/',index), ('/login/',login), ] ######################################################################## '''這一部分可以單獨拿出來,作為框架開發者,框架使用''' def RunServer(environ, start_response): start_response('200 OK', [('Content-Type', 'text/html')]) #根據url的不同,返回不同的字符串 #1 獲取URL[URL從哪里獲取?當請求過來之后執行RunServer,wsgi給咱們封裝了這些請求,這些請求都封裝到了,environ & start_response] request_url = environ['PATH_INFO'] #2 根據URL做不同的相應 #print environ #這里可以通過斷點來查看它都封裝了什么數據 #循環這個列表 for url in url_list: #如果用戶請求的url和咱們定義的rul匹配 if request_url == url[0]: print url return url[1]() #執行里面的方法 else: #url_list列表里都沒有返回404 return '404' if __name__ == '__main__': httpd = make_server('', 8000, RunServer) print "Serving HTTP on port 8000..." httpd.serve_forever()
2、模板引擎
在上一步驟中,對於所有的login、index均返回給用戶瀏覽器一個簡單的字符串,在現實的Web請求中一般會返回一個復雜的符合HTML規則的字符串,所以我們一般將要返回給用戶的HTML寫在指定文件中,然后再返回。如:
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> </head> <body> <h1>Index</h1> </body> </html>
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> </head> <body> <form> <input type="text" /> <input type="text" /> <input type="submit" /> </form> </body> </html>
#!/usr/bin/env python #-*- coding:utf-8 -*- from wsgiref.simple_server import make_server def index(): #讀取html並返回 data = open('html/index.html').read() return data def login(): #讀取html並返回 data = open('html/login.html').read() return data #1 定義一個列表,上面定義函數 url_list = [ #這里吧URL和函數做一個對應 ('/index/',index), ('/login/',login), ] def RunServer(environ, start_response): start_response('200 OK', [('Content-Type', 'text/html')]) #根據url的不同,返回不同的字符串 #1 獲取URL[URL從哪里獲取?當請求過來之后執行RunServer,wsgi給咱們封裝了這些請求,這些請求都封裝到了,environ & start_response] request_url = environ['PATH_INFO'] #2 根據URL做不同的相應 #print environ #這里可以通過斷點來查看它都封裝了什么數據 #循環這個列表 for url in url_list: #如果用戶請求的url和咱們定義的rul匹配 if request_url == url[0]: print url return url[1]() #執行里面的方法 else: #url_list列表里都沒有返回404 return '404' if __name__ == '__main__': httpd = make_server('', 8000, RunServer) print "Serving HTTP on port 8000..." httpd.serve_forever()
對於上述代碼,雖然可以返回給用戶HTML的內容以現實復雜的頁面,但是還是存在問題:如何給用戶返回動態內容?
- 自定義一套特殊的語法,進行替換
- 使用開源工具jinja2,遵循其指定語法
index.html 遵循jinja語法進行替換、循環、判斷
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <!--general replace--> <h1>{{ name }}</h1> <h1>{{ age }}</h1> <h1>{{ time }}</h1> <!--for circular replace--> <ul> {% for iterm in user_list %} <li>{{ iterm }}</li> {% endfor %} </ul> <!--if else judge--> {% if num == 1%} <h1>1111</h1> {% else %} <h1>2222</h1> {% endif %} </body> </html>
web.py
#!/usr/bin/env python #-*- coding:utf-8 -*- import time from wsgiref.simple_server import make_server from jinja2 import Template def index(): data = open('html/index.html').read() template = Template(data) result = template.render( name = 'luotianshuai', age = '18', time = str(time.time()), user_list = ['tianshuai','tim','shuaige'], num = 1 ) #同樣是替換為什么用jinja,因為他不僅僅是文本的他還支持if判斷 & for循環 操作 #這里需要注意因為默認是的unicode的編碼所以設置為utf-8 return result.encode('utf-8') def login(): #讀取html並返回 data = open('html/login.html').read() return data #1 定義一個列表,上面定義函數 url_list = [ #這里吧URL和函數做一個對應 ('/index/',index), ('/login/',login), ] def RunServer(environ, start_response): start_response('200 OK', [('Content-Type', 'text/html')]) #根據url的不同,返回不同的字符串 #1 獲取URL[URL從哪里獲取?當請求過來之后執行RunServer,wsgi給咱們封裝了這些請求,這些請求都封裝到了,environ & start_response] request_url = environ['PATH_INFO'] #2 根據URL做不同的相應 #print environ #這里可以通過斷點來查看它都封裝了什么數據 #循環這個列表 for url in url_list: #如果用戶請求的url和咱們定義的rul匹配 if request_url == url[0]: print url return url[1]() #執行里面的方法 else: #url_list列表里都沒有返回404 return '404' if __name__ == '__main__': httpd = make_server('', 8000, RunServer) print "Serving HTTP on port 8000..." httpd.serve_forever()
遵循jinja2的語法規則,其內部會對指定的語法進行相應的替換,從而達到動態的返回內容,對於模板引擎的本質,參考武Sir老師一篇博客:白話tornado源碼之褪去模板外衣的前戲
MVC & MVT
首先咱們寫的web框架為了方便其他人使用,給他進行一個標准(規范)

把所有的路由規則操作的都放到:把操作DD的都放到Model里、操作ulr的放到Controller目錄下(函數)、View存放所有的Html模板
url里存放所有的映射關系就是user_list列表里:(‘index.html’,index)。
這就是MVC框架
首先咱們在看下WEB框架的處理流程
那么MTV就是:
Models 處理DB操作
Templates html模板
Views 處理函數請求


