1. 程式人生 > 其它 >手擼web框架

手擼web框架

手擼web框架

web框架是前端與資料庫的中介。

在我們所常見的web中,一般情況下,一個應用不會只有一個首頁介面,當頂級域名後有其他字尾時,會有其他的web介面,也就是將我們的網頁應用拆分開來,通過不同的字尾可以訪問不同的介面。如圖:

部落格園首頁https://www.cnblogs.com/

隨意點選網頁中的一篇部落格檢視:

socket服務端

我們可以socket寫一個服務端,然後通過http協議請求,通過瀏覽器來訪問到服務端的內容。

然後可以通過辨認請求體的請求首行,來得知網址的字尾,通過不同字尾返回給我們的網頁不同的內容。

請求體對比:

# 首頁  http://127.0.0.1:8080
GET / HTTP/1.1
Host: 127.0.0.1:8080
。。。(一系列鍵值對)
Accept-Language: zh-CN,zh;q=0.9
/r/n
# 字尾一個/index   http://127.0.0.1:8080/index
GET /index HTTP/1.1
。。。(一系列鍵值對)
Accept-Language: zh-CN,zh;q=0.9
/r/n

只要是同一個域名就可以訪問到我的服務端,

而請求首行則會提供給我們請求方式,網址字尾(路由)協議型別,基於路由做判斷,即可分發不同的html內容,達到應用介面劃分的效果。

import socket

server = socket.socket()  # TCP UDP
server.bind(('127.0.0.1', 8080))  # IP PORT
server.listen(5)  # 半連線池

while True:
    sock, address = server.accept()  # 等待連線
    data = sock.recv(1024)  # 位元組(bytes)
    # print(data.decode('utf8'))  # 解碼列印
    sock.send(b'HTTP/1.1 200 OK\r\n\r\n')  # 簡易響應體,有響應首行和空頭、換行
    
    data_str = data.decode('utf8')  # 先轉換成字串
    target_url = data_str.split(' ')[1]  # 按照空格切割字串並取索引1對應的資料
    # print(target_url)  # /index /login /reg
    if target_url == '/index':
        # sock.send(b'index page')
        with open(r'myhtml01.html','rb') as f:  # 可以傳送html檔案
            sock.send(f.read())
    elif target_url == '/login':
        sock.send(b'login page')   # 可以直接傳送二進位制
    else:
        sock.send(b'home page!')

以上,有許多問題叩待解決:

  • socket程式碼過於重複
  • 針對請求資料處理繁瑣
  • 字尾匹配邏輯過於LowB

基於wsgiref模組封裝優化

wsgiref是一個內建模組,它幫我們:

  • 封裝了socket程式碼
  • 處理了請求資料
    所有的請求首行、請求頭所代表的資訊被封裝為一個字典,可以通過鍵值拿資訊。
    如我們的路由(網址字尾)就可以通過request.get('PATH_INFO')來獲取

關於wsgiref模組需要注意以下功能:

關鍵字 功能
request 封裝好的請求體資料字典
response 響應相關資料
make_server(ip,port,app) 監聽該埠,一旦有請求,會將request、response傳給app函式執行
.serve_forever() 將上一個功能返回的服務端物件開啟,進行實時監聽

用wsgiref搭建web

from wsgiref.simple_server import make_server

def run(request, response):
    """
    :param request: 請求相關資料
    :param response: 響應相關資料
    :return: 返回給客戶端的真實資料
    """
    response('200 OK', [])  # 固定格式 不用管它
    # print(request)  是一個處理之後的大字典
    path_info = request.get('PATH_INFO')
    if path_info == '/index':
        return [b'index']
    elif path_info == '/login':
        return [b'login']
    return [b'hello wsgiref module']

if __name__ == '__main__':
    server = make_server('127.0.0.1', 8080, run)  
    # 實時監聽127.0.0.1:8080 一旦有請求過來自動給第三個引數加括號並傳引數呼叫
    server.serve_forever()  # 啟動服務端

仍然沒有解決分頁邏輯很low的問題。

說到處理if分支過多的問題,我們自然可以想到將路由和要返回的html頁面的對應關係存起來,然後為了結構更加清晰,又可以將對應關係存到一個檔案中urls.py,html檔案群放到資料夾templates中。而原本啟動服務端的核心功能就可以命名為view.py檢視檔案。

分模組程式碼
# view.py
from wsgiref.simple_server import make_server
from urls import urls
import templates
import os

BASE_PATH = os.path.dirname(__file__)
TEMP_PATH = os.path.join(BASE_PATH, "templates")


def run(request, response):
    response("200 OK", [])
    path_info = request.get("PATH_INFO")
    html_file = os.path.join(TEMP_PATH, "default.html")
    for ur in urls:
        if ur[0] == path_info:
            html_file = ur[1]
            html_file = os.path.join(TEMP_PATH, html_file)
            print(html_file)
            break
    with open(html_file, 'rb') as f:
        return [f.read()]


if __name__ == '__main__':
    server = make_server("127.0.0.1", 8080, run)
    server.serve_forever()

    
    
# urls.py
urls = [
    ("/index", "index.html"),
    ("/login", "login.html"),
]

# templates
default.html
index.html
login.html

# default.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>default</title>
</head>
<body>
  <h1>404 not found</h1>
</body>
</html>

動靜態網頁

在我們平常接觸的web中,一般都是動態網頁,而我們上述所寫的是靜態網頁

  • 網頁的html檔案資料被寫死了,無法通過程式修改即靜態網頁
  • 網頁的html檔案中的資料來源於後端,每次請求都會讓後端的資料組織到html中返回給使用者的網頁叫做動態網頁。

為了做動態網頁,我們應該想到,後端的資料來源於資料庫,我們可以通過pymysql對接sql和python程式碼,我們還需要將python資料放到頁面中。

jinja2模組處理

那麼如何通過python程式碼向html檔案中放入資料呢,這顯然是一個複雜且重複性高的動作,我們可以通過第三方模組jinja2來相對方便的將python資料動態的嵌入到我們的html檔案中。

接下來的程式中要用到jinja2的一些方法:

關鍵字 功能
Template() 獲取字串,可以是html檔案內容
render 將python資料以字典形式提交給html
html中{} render會識別這些符號進行一些操作。
jinja2匯入資料到html檔案
# view.py

from jinja2 import Template

def get_dict(request, data):
    user_dict = {'name': 'jason', 'pwd': 123, 'hobby': 'read'}
    new_list = [11, 22, 33, 44, 55, 66]
    temp_obj = Template(data)
    res = temp_obj.render({'user': user_dict, 'new_list': new_list})  # 將python資料提交給html檔案
    return res  # 將處理好的文字檔案返回

def run(request, response):
    response("200 OK", [])
    path_info = request.get("PATH_INFO")
    html_file = os.path.join(TEMP_PATH, "default.html")
    for ur in urls:
        if ur[0] == path_info:
            html_file = ur[1]
            html_file = os.path.join(TEMP_PATH, html_file)
            print(html_file)
            break
    with open(html_file, 'r', encoding='utf8') as f:
        return [get_dict(request, f.read()).encode('utf8')]  # 呼叫get_dict函式
    
if __name__ == '__main__':
    server = make_server("127.0.0.1", 8080, run)
    server.serve_forever()
    
# get_dict.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>get_dict</title>
</head>
<body>
  <h1>字典資料展示</h1>
  <p>{{ user }}</p>  <!--對提交進來的資料進行應用 -->
  <p>{{ user.name }}</p>
  <p>{{ user['pwd'] }}</p>
  <p>{{ user.get('hobby') }}</p>
  <h1>列表資料展示</h1>
  <p>
      {% for i in new_list%}
          <span>元素:{{ i }}</span>
      {% endfor %}
  </p>
</body>
</html>