1. 程式人生 > >Python進階之(web框架基礎)

Python進階之(web框架基礎)

\n 開發環境 one tran 處理 nco UNC 提交數據 拼接

一、web框架本質

我們可以這樣理解:所有的Web應用本質上就是一個socket服務端,而用戶的瀏覽器就是一個socket客戶端。 這樣我們就可以自己實現Web框架了。

import socket

sk = socket.socket()
sk.bind(("127.0.0.1", 80))
sk.listen()


while True:
    conn, addr = sk.accept()
    data = conn.recv(8096)
    conn.send(b‘hello world‘)
    conn.close()

可以說Web服務本質上都是在這十幾行代碼基礎上擴展出來的,用戶在瀏覽器一輸入網址,給服務端發送數據,那瀏覽器會發送什麽數據?怎麽發?這個誰來定? 所以,必須有一個統一的規則,讓大家發送消息、接收消息的時候有個格式依據,不能隨便寫。

這個規則就是HTTP協議,以後瀏覽器發送請求信息也好,服務器回復響應信息也罷,都要按照這個規則來。

HTTP協議主要規定了客戶端和服務器之間的通信格式,那HTTP協議是怎麽規定消息格式的呢?

讓我們首先打印下我們在服務端接收到的消息是什麽

import socket

sk = socket.socket()
sk.bind(("127.0.0.1", 80))
sk.listen()


while True:
    conn, addr = sk.accept()
    data = conn.recv(8096)
    print(data)
    conn.send(b‘hello world‘)
    conn.close()

輸出結果:

b‘GET / HTTP/1.1\r\n
Host: 127.0.0.1\r\n
Connection: keep-alive\r\nCache-Control: max-age=0\r\n
Upgrade-Insecure-Requests: 1\r\n
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36\r\n
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\r\nAccept-Encoding: gzip, deflate, br\r\n
Accept-Language: zh-CN,zh;q=0.9\r\n
Cookie: bdshare_firstime=1529473540203; csrftoken=WqST6GYvUUK9qKpkZlMnsbQzue1kOm5LknTvd9GQlr1MOj8yZ9YouYFjKLwgE9lb\r\n\r\n‘

我們發現收發的消息是要按照一定的格式來,這裏就需要了解一下HTTP協議了

HTTP協議(HyperText Transfer Protocol,超文本傳輸協議)是因特網上應用最為廣泛的一種網絡傳輸協議,所有的WWW文件都必須遵守這個標準。

HTTP是一個基於TCP/IP通信協議來傳遞數據(HTML 文件, 圖片文件, 查詢結果等)

HTTP協議定義Web客戶端如何從Web服務器請求Web頁面,以及服務器如何把Web頁面傳送給客戶端。HTTP協議采用了請求/響應模型。客戶端向服務器發送一個請求報文,請求報文包含請求的方法、URL、協議版本、請求頭部和請求數據。服務器以一個狀態行作為響應,響應的內容包括協議的版本、成功或者錯誤代碼、服務器信息、響應頭部和響應數據。然後客戶端瀏覽器解析HTML內容

HTTP請求方法:

GET(向指定的資源發出“顯示”請求。使用GET方法應該只用在讀取數據,而不應當被用於產生“副作用”的操作中,例如在Web Application中。其中一個原因是GET可能會被網絡蜘蛛等隨意訪問)

HEAD(與GET方法一樣,都是向服務器發出指定資源的請求。只不過服務器將不傳回資源的本文部分。它的好處在於,使用這個方法可以在不必傳輸全部內容的情況下,就可以獲取其中“關於該資源的信息”(元信息或稱元數據))

POST(向指定資源提交數據,請求服務器進行處理(例如提交表單或者上傳文件)。數據被包含在請求本文中。這個請求可能會創建新的資源或修改現有資源,或二者皆有)

PUT(向指定資源位置上傳其最新內容)

DELETE(請求服務器刪除Request-URI所標識的資源)

TRACE(回顯服務器收到的請求,主要用於測試或診斷)

OPTIONS(這個方法可使服務器傳回該資源所支持的所有HTTP請求方法。用‘*‘來代替資源名稱,向Web服務器發送OPTIONS請求,可以測試服務器功能是否正常運作)

CONNECT(HTTP/1.1協議中預留給能夠將連接改為管道方式的代理服務器。通常用於SSL加密服務器的鏈接(經由非加密的HTTP代理服務器))

HTTP狀態碼:

  • 1xx消息——請求已被服務器接收,繼續處理
  • 2xx成功——請求已成功被服務器接收、理解、並接受
  • 3xx重定向——需要後續操作才能完成這一請求
  • 4xx請求錯誤——請求含有詞法錯誤或者無法被執行
  • 5xx服務器錯誤——服務器在處理某個正確請求時發生錯誤

HTTP協議對收發消息的格式要求:

HTTP 請求格式:

技術分享圖片

HTTP響應格式:

技術分享圖片

二、完善自定義的web框架

import socket
#index頁面
def f1(request):
    f = open(‘index.txt‘,‘rb‘)
    data = f.read()
    f.close()
    return data
#home頁面
def f2(request):
    import pymysql
    # 使用pymsql連接數據庫
    conn = pymysql.connect(host=‘127.0.0.1‘, port=3306, user=‘root‘, passwd=‘‘, db=‘userbase‘)
    cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
    cursor.execute("select id,username,password from userinfo")
    user_list = cursor.fetchall() #拿出數據
    cursor.close()
    conn.close()

    content_list = []
    for row in user_list:
        tp = "<tr><td>%s</td><td>%s</td><td>%s</td></tr>" %(row[‘id‘],row[‘username‘],row[‘password‘])
        content_list.append(tp)
    content = "".join(content_list)
f = open(‘home.html‘,‘r‘,encoding=‘utf-8‘) template = f.read() f.close() # 模板渲染 data = template.replace(‘@@userlist@@‘,content) #替換掉原有文件裏面的特殊符號內容 return bytes(data,encoding=‘utf-8‘) routers = [ (‘/index‘, f1), (‘/home.html‘, f2), ] def runserver(): sock = socket.socket() sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.bind((‘127.0.0.1‘,80)) sock.listen(5) while True: conn,addr = sock.accept() data = conn.recv(8096) data = str(data,encoding=‘utf-8‘) headers,bodys = data.split(‘\r\n\r\n‘) temp_list = headers.split(‘\r\n‘) method,url,protocal = temp_list[0].split(‘ ‘) func_name = None for item in routers: if item[0] == url: func_name = item[1] break if func_name: response = func_name(data) else: response = b"404" conn.send(response) conn.close() if __name__ == ‘__main__‘: runserver()

三、wsgiref

對於真實開發中的python web程序來說,一般會分為兩部分:服務器程序和應用程序。服務器程序負責對socket服務器進行封裝,並在請求到來時,對請求的各種數據進行整理。應用程序則負責具體的邏輯處理。為了方便應用程序的開發,就出現了眾多的Web框架,例如:Django、Flask、web.py 等。不同的框架有不同的開發方式,但是無論如何,開發出的應用程序都要和服務器程序配合,才能為用戶提供服務。這樣,服務器程序就需要為不同的框架提供不同的支持。這樣混亂的局面無論對於服務器還是框架,都是不好的。對服務器來說,需要支持各種不同框架,對框架來說,只有支持它的服務器才能被開發出的應用使用。這時候,標準化就變得尤為重要。我們可以設立一個標準,只要服務器程序支持這個標準,框架也支持這個標準,那麽他們就可以配合使用。一旦標準確定,雙方各自實現。這樣,服務器可以支持更多支持標準的框架,框架也可以使用更多支持標準的服務器。

WSGI(Web Server Gateway Interface)就是一種規範,它定義了使用Python編寫的web應用程序與web服務器程序之間的接口格式,實現web應用程序與web服務器程序間的解耦。常用的WSGI服務器有uwsgi、Gunicorn。而Python標準庫提供的獨立WSGI服務器叫wsgiref,Django開發環境用的就是這個模塊來做服務器

from wsgiref.simple_server import make_server
def run_server(environ, start_response):
    start_response(‘200 OK‘, [(‘Content-Type‘, ‘text/html;charset=utf8‘), ])  
   # 設置HTTP響應的狀態碼和頭信息
    url = environ[‘PATH_INFO‘]  # 取到用戶輸入的url,可進行判斷取舍

if __name__ == ‘__main__‘:
    httpd = make_server(‘127.0.0.1‘, 8090, run_server)
    print("開始服務...")
    httpd.serve_forever()

四、jinja2

上面的自定義web框架中,我們使用字符串拼接後用特殊符號替換來完成html的渲染,其實模板渲染有個現成的工具: jinja2

下載安裝:pip install jinja2
from jinja2 import Template
def index():
    userlist=[{"id":1,"username":‘jump‘,"age":12}]
    with open("index.html", "r") as f:
        data = f.read()
    template = Template(data)  # 生成模板文件
    ret = template.render(index=userlist)  # 把數據填充到模板裏面
    return [bytes(ret, encoding="utf8"), ]


#html頁面部分
{% for row in index %}
      <tr>
           <td>{{row.id}}</td>
           <td>{{row.username}}</td>
            <td>{{row.age}}</td>
      </tr>
{% endfor %}

  

補充:

自定義web框架:

  1. socket服務端
  2. 根據URL不同返回不同的內容(路由系統:URL -> 函數)
  3. 結果返回給用戶(模板引擎渲染:自己創造任意數據替換,jinja2)

Web框架:
根據上述3點web框架分類:
- 1,2,3 --> Tornado
- [第三方1],2,3 --> wsgiref -> Django
- [第三方1],2,[第三方3] --> flask

Python進階之(web框架基礎)