一、Web框架本質
眾所周知,對於所有的Web應用,本質上其實就是一個socket服務端,用戶的瀏覽器其實就是一個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, Seven")
def main():
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('localhost',8000))
sock.listen(5)
while True:
connection, address = sock.accept()
handle_request(connection)
connection.close()
if __name__ == '__main__':
main()
上述通過socket來實現了其本質,而對於真實開發中的python web程序來說,一般會分為兩部分:服務器程序和應用程序。服務器程序負責對socket服務器進行封裝,並在請求到來時,對請求的各種數據進行整理。應用程序則負責具體的邏輯處理。為了方便應用程序的開發,就出現了眾多的Web框架,例如:Django、Flask、web.py 等。不同的框架有不同的開發方式,但是無論如何,開發出的應用程序都要和服務器程序配合,才能為用戶提供服務。這樣,服務器程序就需要為不同的框架提供不同的支持。這樣混亂的局面無論對於服務器還是框架,都是不好的。對服務器來說,需要支持各種不同框架,對框架來說,只有支持它的服務器才能被開發出的應用使用。這時候,標准化就變得尤為重要。我們可以設立一個標准,只要服務器程序支持這個標准,框架也支持這個標准,那么他們就可以配合使用。一旦標准確定,雙方各自實現。這樣,服務器可以支持更多支持標准的框架,框架也可以使用更多支持標准的服務器。
二、WSGI
WSGI(Web Server Gateway Interface)是一種規范,它定義了使用python編寫的web app與web server之間接口格式,實現web app與web server間的解耦。
python標准庫提供的獨立WSGI服務器稱為wsgiref。當還有其他的接口格式:
'cgi': CGIServer, 'flup': FlupFCGIServer, 'wsgiref': WSGIRefServer, 'waitress': WaitressServer, 'cherrypy': CherryPyServer, 'paste': PasteServer, 'fapws3': FapwsServer, 'tornado': TornadoServer, 'gae': AppEngineServer, 'twisted': TwistedServer, 'diesel': DieselServer, 'meinheld': MeinheldServer, 'gunicorn': GunicornServer, 'eventlet': EventletServer, 'gevent': GeventServer, 'geventSocketIO':GeventSocketIOServer, 'rocket': RocketServer, 'bjoern' : BjoernServer, 'auto': AutoServer,
Django實現的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 world!</h1>'
if __name__ == '__main__':
httpd = make_server('', 8000, RunServer)
print "Serving HTTP on port 8000..."
httpd.serve_forever()
三、自定義Web框架
1、框架
通過python標准庫提供的wsgiref模塊開發一個自己的Web框架
#!/usr/bin/env python
#coding:utf-8
from wsgiref.simple_server import make_server
def index():
return 'index'
def login():
return 'login'
def routers():
urlpatterns = (
('/index/',index),
('/login/',login),
)
return urlpatterns
def RunServer(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/html')])
url = environ['PATH_INFO']
urlpatterns = routers()
func = None
for item in urlpatterns:
if item[0] == url:
func = item[1]
break
if func:
return func()
else:
return '404 not found'
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():
# return 'index'
f = open('index.html')
data = f.read()
return data
def login():
# return 'login'
f = open('login.html')
data = f.read()
return data
def routers():
urlpatterns = (
('/index/', index),
('/login/', login),
)
return urlpatterns
def run_server(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/html')])
url = environ['PATH_INFO']
urlpatterns = routers()
func = None
for item in urlpatterns:
if item[0] == url:
func = item[1]
break
if func:
return func()
else:
return '404 not found'
if __name__ == '__main__':
httpd = make_server('', 8000, run_server)
print "Serving HTTP on port 8000..."
httpd.serve_forever()
對於上述代碼,雖然可以返回給用戶HTML的內容以現實復雜的頁面,但是還是存在問題:如何給用戶返回動態內容?
- 自定義一套特殊的語法,進行替換
- 使用開源工具jinja2,遵循其指定語法
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> </head> <body> <h1>{{name}}</h1> <ul> {% for item in user_list %} <li>{{item}}</li> {% endfor %} </ul> </body> </html>
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from wsgiref.simple_server import make_server
from jinja2 import Template
def index():
# return 'index'
# template = Template('Hello {{ name }}!')
# result = template.render(name='John Doe')
f = open('index.html')
result = f.read()
template = Template(result)
data = template.render(name='John Doe', user_list=['alex', 'eric'])
return data.encode('utf-8')
def login():
# return 'login'
f = open('login.html')
data = f.read()
return data
def routers():
urlpatterns = (
('/index/', index),
('/login/', login),
)
return urlpatterns
def run_server(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/html')])
url = environ['PATH_INFO']
urlpatterns = routers()
func = None
for item in urlpatterns:
if item[0] == url:
func = item[1]
break
if func:
return func()
else:
return '404 not found'
if __name__ == '__main__':
httpd = make_server('', 8000, run_server)
print "Serving HTTP on port 8000..."
httpd.serve_forever()
遵循jinja2的語法規則,其內部會對指定的語法進行相應的替換,從而達到動態的返回內容,對於模板引擎的本質,參考:白話tornado源碼之褪去模板外衣的前戲
四、MVC與MTV
MVC模式:
MTV模式:
- M 代表模型(Model):負責業務對象和數據庫的關系映射(ORM)。
- T 代表模板 (Template):負責如何把頁面展示給用戶(html)。
- V 代表視圖(View):負責業務邏輯,並在適當時候調用Model和Template。
- Web服務器(中間件)收到一個http請求
- Django在URLconf里查找對應的視圖(View)函數來處理http請求
- 視圖函數調用相應的數據模型來存取數據、調用相應的模板向用戶展示頁面
- 視圖函數處理結束后返回一個http的響應給Web服務器
- Web服務器將響應發送給客戶端

比如,開發者更改一個應用程序中的 URL 而不用影響到這個程序底層的實現。設計師可以改變 HTML頁面的樣式而不用接觸Python代碼。
數據庫管理員可以重新命名數據表並且只需更改模型,無需從一大堆文件中進行查找和替換。
