Python之路【第十五篇】WEB框架


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>
index.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>
login.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    處理函數請求

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM