1. 程式人生 > 其它 >DIY一個web框架

DIY一個web框架

web框架

Web框架(Web framework)是一種開發框架,用來支援動態網站、網路應用和網路服務的開發。這大多數的web框架提供了一套開發和部署網站的方式,也為web行為提供了一套通用的方法。web框架已經實現了很多功能,開發人員使用框架提供的方法並且完成自己的業務邏輯,就能快速開發web應用了。瀏覽器和伺服器的是基於HTTP協議進行通訊的。也可以說web框架就是在以上十幾行程式碼基礎張擴展出來的,有很多簡單方便使用的方法,大大提高了開發的效率。

wsgiref模組

最簡單的Web應用就是先把HTML用檔案儲存好,用一個現成的HTTP伺服器軟體,接收使用者請求,從檔案中讀取HTML,返回。

如果要動態生成HTML,就需要把上述步驟自己來實現。不過,接受HTTP請求、解析HTTP請求、傳送HTTP響應都是苦力活,如果我們自己來寫這些底層程式碼,還沒開始寫動態HTML呢,就得花個把月去讀HTTP規範。

正確的做法是底層程式碼由專門的伺服器軟體實現,我們用Python專注於生成HTML文件。因為我們不希望接觸到TCP連線、HTTP原始請求和響應格式,所以,需要一個統一的介面協議來實現這樣的伺服器軟體,讓我們專心用Python編寫Web業務。這個介面就是WSGI:Web Server Gateway Interface。而wsgiref模組就是python基於wsgi協議開發的服務模組。

from wsgiref.simple_server import make_server


def application(environ, start_response):
    # 按著http協議解析資料:environ---請求
    # 按著http協議組裝資料:start_response---響應
    # print(environ)
    # print(type(environ))  # <class 'dict'>
    start_response("200 OK", [("Content-type", "text-html")])  # 這個格式是固定的,必須是這樣(響應狀態碼, [(響應頭), (響應頭)...])
# 得到請求路徑 path = environ.get("PATH_INFO") # 對請求路徑進行判斷 if path == "/login": return [b"<h1>login</h1>"] # 返回必須是列表型別 elif path == "/index": return [b"<h1>index</h1>"] # 返回必須是列表型別 else: return [b"<h1>hello world</h1>"] # 返回必須是列表型別 # 封裝socket httped = make_server("", 8090, application) # 等待使用者連線:conn, addr = sock.accept() httped.serve_forever() # 會呼叫application(environ, start_response)

DIY一個web框架

models.py

# 在資料庫生成使用者表,在專案執行前只需執行一次即可,把所有建立資料表的操作都放在此檔案即可
import pymysql


conn = pymysql.connect(
    host="127.0.0.1",
    port=3306,
    user="root",
    password="123456",
    db="web",
    autocommit=True
)

cur = conn.cursor()

sql = """
create table user_info(
    id int primary key auto_increnment not null,
    name varchar(32) not null,
    pwd varchar(32) not null
"""

cur.execute(sql)

cur.close()
conn.close()

main.py

from wsgiref.simple_server import make_server
from urls import url_patterns


def application(environ, start_response):
    start_response("200 OK", [("Content-type", "text/html")])
    print(environ.get("PATH_INFO"))
    # 客戶端請求路徑
    path = environ.get("PATH_INFO")

    # 方案1---直接針對路徑做判斷,然後寫業務邏輯程式碼
    # if path == "/login":
    #     with open("login.html", "rb") as f:
    #         data = f.read()
    #     return [data]
    # elif path == "/index":
    #     with open("index.html", "rb") as f:
    #         data = f.read()
    #     return [data]

    # 方案2---將業務處理邏輯封裝成函式,與路徑一一匹配放在元組,然後用一個列表放置所有的元組
    # 這樣就不用頻繁的使用if判斷,而且解耦後,如果想增加一個路徑,只需要新增一個對應檢視函式即可
    # 把url_patterns(路由分發)列表放在urls.py
    # 把檢視函式放在views.py
    # 把所有html檔案放在templates資料夾下
    func = None
    # 迴圈列表,取出路徑和對應檢視函式地址
    for p, f in url_patterns:
        # 如果瀏覽器請求路徑正確,那麼把路徑對應的檢視函式地址賦值給func
        if path == p:
            func = f
            break
    # 如果func不為空,那麼執行func,並且把客戶端請求資訊作為引數,返回func執行後的結果給客戶端,如果為空則返回錯誤資訊
    if func:
        return [func(environ)]
    else:
        return [b"404 error"]


httped = make_server("", 8080, application)

httped.serve_forever()

urls.py

from views import *


url_patterns
= [ ("/login", login), ("/reg", reg), ("/auth", auth), ]

views.py

import pymysql
from urllib.parse import parse_qs


def login(environ):
    with open("templates/login.html", "rb") as f:
        data = f.read()
    return data


def reg(environ):
    with open("templates/reg.html", "rb") as f:
        data = f.read()
    return data


def auth(environ):
    """
    以下操作是wsgiref模組讀取form表單提交的資料,比Django複雜,但比我們自己處理起來要簡單很多了
    :param environ:
    :return:
    """
    try:
        request_body_size = int(environ.get("CONTENT_LENGTH", 0))
    except (ValueError):
        request_body_size = 0

    request_body = environ['wsgi.input'].read(request_body_size)
    data = parse_qs(request_body)
    user = data.get(b"user")[0].decode("utf8")
    pwd = data.get(b"pwd")[0].decode("utf8")
    print(user, pwd)
    # 操作資料庫
    conn = pymysql.connect(
        host="127.0.0.1",
        port=3306,
        user="root",
        password="123456",
        db="web",
    )
    cur = conn.cursor()
    sql = "select * from user_info where name=%s and pwd=%s;"
    cur.execute(sql, (user, pwd))
    # 如果使用者名稱密碼正確,那麼返回index頁面,不正確返回錯誤資訊
    if cur.fetchone():
        f = open("templates/index.html", "rb")
        data = f.read()
        return data
    else:
        return b"user or pwd is wrong"

login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登入</title>
    <link rel="icon" href="//www.jd.com/favicon.ico">
</head>
<body>
<h3>登入頁面</h3>
<!--向auth路徑提交form表單-->
<form action="http://127.0.0.1:8080/auth" method="post">
    <p>
        姓名:
        <input type="text" name="user">
    </p>
    <p>
        密碼:
        <input type="password" name="pwd">
    </p>
    <p>
        <input type="submit" value="提交">
    </p>
</form>

</body>
</html>

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>首頁</title>
    <link rel="icon" href="//www.jd.com/favicon.ico">
</head>
<body>
<h1>首頁</h1>
<img src="https://img0.baidu.com/it/u=2641803730,2025967347&fm=26&fmt=auto" alt="">
</body>
</html>

總結

"""
web框架功能總結
  1. main.py:啟動檔案,封裝了socket
  2. urls.py:路徑與檢視函式對映關係---url控制器
  3. views.py:檢視函式,固定有一個形式引數environ(所有的請求資訊都在這個引數裡面,請求路徑/請求方式/請求資料...)---檢視函式
  4. templates資料夾:html檔案---模板
  5. models:在專案啟動前,在資料庫中建立表結構---與資料庫相關
"""
while True: print('studying...')