Web框架本質

Web框架本質

咱們能夠這樣理解:全部的Web應用本質上就是一個socket服務端,而用戶的瀏覽器就是一個socket客戶端。 這樣咱們就能夠本身實現Web框架了。html

import socket
#實例化服務端對象
sk = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#爲對象綁定ip地址和端口號
sk.bind(('127.0.0.1',80))
#監聽端口
sk.listen(5)
while 1:
    #獲取客戶端鏈接對象和客戶端地址信息
    conn,addr = sk.accept()
    #獲取客戶端發送的信息
    data = conn.recv(9000)
    #響應客戶端發送響應行()
    conn.send(b'HTTP/1.1 200 OK\r\n\r\n')
    #發送響應數據
    conn.send(b'<h1>hello kingfan!</h1>')
    #關閉與客戶端鏈接
    conn.close()

能夠說Web服務本質上都是在這十幾行代碼基礎上擴展出來的。這段代碼就是它們的祖宗。python

用戶的瀏覽器一輸入網址,會給服務端發送數據,那瀏覽器會發送什麼數據?怎麼發?這個誰來定? 你這個網站是這個規定,他那個網站按照他那個規定,這互聯網還能玩麼?mysql

因此,必須有一個統一的規則,讓你們發送消息、接收消息的時候有個格式依據,不能隨便寫。web

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

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

讓咱們首先打印下咱們在服務端接收到的消息是什麼。瀏覽器

import socket
#實例化服務端對象
sk = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#爲對象綁定ip地址和端口號
sk.bind(('127.0.0.1',80))
#監聽端口
sk.listen(5)
while 1:
    #獲取客戶端鏈接對象和客戶端地址信息
    conn,addr = sk.accept()
    #獲取客戶端發送的信息
    data = conn.recv(9000)
    print(data)
    #響應客戶端發送響應行()
    conn.send(b'HTTP/1.1 200 OK\r\n\r\n')
    #發送響應數據
    conn.send(b'<h1>hello kingfan!</h1>')
    #關閉與客戶端鏈接
    conn.close()
b'GET / HTTP/1.1\r\nHost: 127.0.0.1:8080\r\nConnection: keep-alive\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\r\nDNT: 1\r\nAccept-Encoding: gzip, deflate, br\r\nAccept-Language: zh-CN,zh;q=0.9\r\nCookie: csrftoken=RKBXh1d3M97iz03Rpbojx1bR6mhHudhyX5PszUxxG3bOEwh1lxFpGOgWN93ZH3zv\r\n\r\n'

基礎自定義web框架

通過上面的補充學習,咱們知道了要想讓咱們本身寫的web server端正經起來,必需要讓咱們的Web server在給客戶端回覆消息的時候按照HTTP協議的規則加上響應狀態行,這樣咱們就實現了一個正經的Web框架了服務器

import socket
#實例化服務端對象
sk = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#爲對象綁定ip地址和端口號
sk.bind(('127.0.0.1',80))
#監聽端口
sk.listen(5)
while 1:
    #獲取客戶端鏈接對象和客戶端地址信息
    conn,addr = sk.accept()
    #獲取客戶端發送的信息
    data = conn.recv(9000)
    print(data)
    #響應客戶端發送響應行()
    conn.send(b'HTTP/1.1 200 OK\r\n\r\n')
    #發送響應數據
    conn.send(b'<h1>hello kingfan!</h1>')
    #關閉與客戶端鏈接
    conn.close()

咱們經過十幾行代碼簡單地演示了web 框架的本質。app

接下來就讓咱們繼續完善咱們的自定義web框架吧框架

根據不一樣的路徑返回不一樣的內容

這樣就結束了嗎? 如何讓咱們的Web服務根據用戶請求的URL不一樣而返回不一樣的內容呢?

小事一樁,咱們能夠從請求相關數據裏面拿到請求URL的路徑,而後拿路徑作一個判斷...

import socket

sk = socket.socket()
sk.bind(('127.0.0.1', 8080))
sk.listen(5)


def login():
    return bytes('歡迎來到登陸頁面', encoding='utf8')


def register():
    return bytes('歡迎來到註冊界面', encoding='utf8')


def home():
    return bytes('歡迎來到主頁面', encoding='utf8')


# 返回一個html文件
def web():
    with open('login.html', 'rb') as f1:
        return f1.read()


url_lis = [('/login/', login),
           ('/register/', register),
           ('/home/', home),
           ('/web/', web)
           ]
while 1:
    conn, addr = sk.accept()
    # 接受請求消息
    data = conn.recv(9000)
    data = data.decode('utf-8')
    # 獲取url是什麼
    url = (data.split('\r\n')[0].split(' ')[1])
    # 在本地url列表中查找url地址有則執行相應的函數
    for i in url_lis:
        if url == i[0]:
            fun = i[1]
            msg = fun()
            break
    # 若是沒有則發送錯誤信息
    else:
        msg = b'404 not found'
    print(msg)
    # 發送響應狀態(Content-Type: text/html; charset=utf-8)響應頭指定返回數據類型和編碼格式
    conn.send(b'HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\n\r\n')
    # 發送響應數據
    conn.send(msg)
    conn.close()

讓網頁動態起來

這網頁可以顯示出來了,可是都是靜態的啊。頁面的內容都不會變化的,我想要的是動態網站。

沒問題,我也有辦法解決。我選擇使用字符串替換來實現這個需求。(這裏使用時間戳來模擬動態的數據)

import socket, time

sk = socket.socket()
sk.bind(('127.0.0.1', 8080))
sk.listen(5)


def login():
    return bytes('歡迎來到登陸頁面', encoding='utf8')


def register():
    return bytes('歡迎來到註冊界面', encoding='utf8')


def home():
    return bytes('歡迎來到主頁面', encoding='utf8')


def nowtime():
    # 將time.html文件中的XX-XX-XX替換成如今時間
    with open('time.html', 'r', encoding='utf-8') as f1:
        msg = f1.read()
        msg = msg.replace('XX-XX-XX', str(time.strftime("%c")))
        return bytes(msg, encoding='utf-8')


# 返回一個html文件
def web():
    with open('login.html', 'rb') as f1:
        return f1.read()


url_lis = [('/login/', login),
           ('/register/', register),
           ('/home/', home),
           ('/web/', web),
           ('/time/', nowtime)
           ]
while 1:
    conn, addr = sk.accept()
    # 接受請求消息
    data = conn.recv(9000)
    data = data.decode('utf-8')
    # 獲取url是什麼
    url = (data.split('\r\n')[0].split(' ')[1])
    # 在本地url列表中查找url地址有則執行相應的函數
    for i in url_lis:
        if url == i[0]:
            fun = i[1]
            msg = fun()
            break
    # 若是沒有則發送錯誤信息
    else:
        msg = b'404 not found'
    print(msg)
    # 發送響應狀態(Content-Type: text/html; charset=utf-8)響應頭指定返回數據類型和編碼格式
    conn.send(b'HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\n\r\n')
    # 發送響應數據
    conn.send(msg)
    conn.close()

服務器程序和應用程序

對於真實開發中的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開發環境用的就是這個模塊來作服務器。

從這繼續...

wsgiref

咱們利用wsgiref模塊來替換咱們本身寫的web框架的socket server部分:

import time
from wsgiref.simple_server import make_server


def login():
    return bytes('歡迎來到登陸頁面', encoding='utf8')


def register():
    return bytes('歡迎來到註冊界面', encoding='utf8')


def home():
    return bytes('歡迎來到主頁面', encoding='utf8')


def nowtime():
    # 將time.html文件中的XX-XX-XX替換成如今時間
    with open('time.html', 'r', encoding='utf-8') as f1:
        msg = f1.read()
        msg = msg.replace('XX-XX-XX', str(time.strftime("%c")))
        return bytes(msg, encoding='utf-8')


# 返回一個html文件
def web():
    with open('login.html', 'rb') as f1:
        return f1.read()


url_lis = [('/login/', login),
           ('/register/', register),
           ('/home/', home),
           ('/web/', web),
           ('/time/', nowtime)
           ]


def run(environ, start_response):
    # 設置HTTP響應的狀態ma碼和頭信息(指定返回類型和編碼格式)
    start_response('200 OK', [('Content-Type', 'text/html;charset=utf8'), ])
    # 獲取用戶訪問的url 本質返回跟前例子同樣如/time/
    url = environ['PATH_INFO']
    for i in url_lis:
        if url == i[0]:
            fun = i[1]
            msg = fun()
            break
    # 若是沒有則發送錯誤信息
    else:
        msg = b'404 not found'
    # wsgire模塊將msg返回給客戶,底層依舊是socket
    return [msg, ]


if __name__ == '__main__':
    httpd = make_server('127.0.0.1', 8080, run)
    print('等待鏈接')
    httpd.serve_forever()

jinja2

上面的代碼實現了一個簡單的動態,我徹底能夠從數據庫中查詢數據,而後去替換我html中的對應內容,而後再發送給瀏覽器完成渲染。 這個過程就至關於HTML模板渲染數據。 本質上就是HTML內容中利用一些特殊的符號來替換要展現的數據。 我這裏用的特殊符號是我定義的,其實模板渲染有個現成的工具: jinja2

import time,pymysql
from wsgiref.simple_server import make_server
from jinja2 import Template

def login():
    return bytes('歡迎來到登陸頁面', encoding='utf8')


def register():
    return bytes('歡迎來到註冊界面', encoding='utf8')


def home():
    return bytes('歡迎來到主頁面', encoding='utf8')


def nowtime():
    # 將time.html文件中的XX-XX-XX替換成如今時間
    with open('time.html', 'r', encoding='utf-8') as f1:
        msg = f1.read()
        msg = msg.replace('XX-XX-XX', str(time.strftime("%c")))
        return bytes(msg, encoding='utf-8')

def userinfo():
    conn = pymysql.connect(
        host = '127.0.0.1',
        port =3306,
        user = 'root',
        password='1234',
        database='db1',
        charset = 'utf8'
    )
    cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
    cursor.execute('select * from staff_info;')
    user_list1 = cursor.fetchall()
    print(user_list1)
    with open('userinfo.html', 'r', encoding='utf8') as f1:
        data = f1.read()
        template = Template(data)
        msg = template.render({'user_list':user_list1})
        return bytes(msg, encoding="utf8")
# 返回一個html文件
def web():
    with open('login.html', 'rb') as f1:
        return f1.read()

url_lis = [('/login/', login),
           ('/register/', register),
           ('/home/', home),
           ('/web/', web),
           ('/time/', nowtime),
           ('/userinfo/',userinfo)
           ]
def run(environ, start_response):
    # 設置HTTP響應的狀態ma碼和頭信息(指定返回類型和編碼格式)
    start_response('200 OK', [('Content-Type', 'text/html;charset=utf8'), ])
    # 獲取用戶訪問的url 本質返回跟前例子同樣如/time/
    url = environ['PATH_INFO']
    print(url)
    for i in url_lis:
        if url == i[0]:
            fun = i[1]
            msg = fun()
            break
    # 若是沒有則發送錯誤信息
    else:
        msg = b'404 not found'
    # wsgire模塊將msg返回給客戶,底層依舊是socket
    return [msg, ]


if __name__ == '__main__':
    httpd = make_server('127.0.0.1', 8080, run)
    print('等待鏈接')
    httpd.serve_forever()
相關文章
相關標籤/搜索