日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Web框架的原理

發(fā)布時間:2025/3/15 编程问答 20 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Web框架的原理 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

Web框架本質(zhì)

我們可以這樣理解:所有的Web應(yīng)用本質(zhì)上就是一個socket服務(wù)端,而用戶的瀏覽器就是一個socket客戶端。 這樣我們就可以自己實現(xiàn)Web框架了。

socket服務(wù)端?

  • 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"OK")??
  • ????conn.close()??
  • 可以說Web服務(wù)本質(zhì)上都是在這十幾行代碼基礎(chǔ)上擴(kuò)展出來的。這段代碼就是它們的祖宗。

    用戶在瀏覽器中輸入網(wǎng)址,瀏覽器會向服務(wù)端發(fā)送數(shù)據(jù),那瀏覽器會發(fā)送什么數(shù)據(jù)?怎么發(fā)?這個誰來定? 你這個網(wǎng)站是這個規(guī)定,他那個網(wǎng)站按照他那個規(guī)定,那互聯(lián)網(wǎng)還能玩么?

    所以,必須有一個統(tǒng)一的規(guī)則,讓大家發(fā)送消息、接收消息的時候都有個格式依據(jù),不能隨便寫。

    這個規(guī)則就是HTTP協(xié)議,以后瀏覽器發(fā)送請求信息也好,服務(wù)器回復(fù)響應(yīng)信息也罷,都要按照這個規(guī)則來。

    HTTP協(xié)議主要規(guī)定了客戶端和服務(wù)器之間的通信格式,那HTTP協(xié)議是怎么規(guī)定消息格式的呢?

    讓我們首先打印下我們在服務(wù)端接收到的消息是什么。

  • 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)??#?將瀏覽器發(fā)來的消息打印出來????
  • ????conn.send(b"OK")????
  • ????conn.close()????
  • 輸出:

    b'GET / HTTP/1.1\r\nHost: 127.0.0.1:8080\r\nConnection: keep-alive\r\nCache-Control: max-age=0\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3355.4 Safari/537.36\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\r\nAccept-Encoding: gzip, deflate, br\r\nAccept-Language: zh-CN,zh;q=0.9\r\nCookie: csrftoken=CtHePYARJOKNx5oNVwxIteOJXpNyJ29L4bW4506YoVqFaIFFaHm0EWDZqKmw6Jm8\r\n\r\n'

    我們將\r\n替換成換行看得更清晰點:

  • GET?/?HTTP/1.1??
  • Host:?127.0.0.1:8080??
  • Connection:?keep-alive??
  • Cache-Control:?max-age=0??
  • Upgrade-Insecure-Requests:?1??
  • User-Agent:?Mozilla/5.0?(Windows?NT?6.1;?WOW64)?AppleWebKit/537.36?(KHTML,?like?Gecko)?Chrome/66.0.3355.4?Safari/537.36??
  • Accept:?text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8??
  • Accept-Encoding:?gzip,?deflate,?br??
  • Accept-Language:?zh-CN,zh;q=0.9??
  • Cookie:?csrftoken=CtHePYARJOKNx5oNVwxIteOJXpNyJ29L4bW4506YoVqFaIFFaHm0EWDZqKmw6Jm8??
  • ??
  • ??
  • 然后我們再看一下我們訪問博客園官網(wǎng)時瀏覽器收到的響應(yīng)信息是什么。

    響應(yīng)相關(guān)信息可以在瀏覽器調(diào)試窗口的Network標(biāo)簽頁中看到。

    點擊view source之后顯示如下圖:

    我們發(fā)現(xiàn)收發(fā)的消息需要按照一定的格式來,這里就需要了解一下HTTP協(xié)議了。

    HTTP協(xié)議介紹

    HTTP協(xié)議對收發(fā)消息的格式要求

    每個HTTP請求和響應(yīng)都遵循相同的格式,一個HTTP包含Header和Body兩部分,其中Body是可選的。

    HTTP響應(yīng)的Header中有一個 Content-Type表明響應(yīng)的內(nèi)容格式。它的值如text/html; charset=utf-8。

    text/html則表示是網(wǎng)頁,charset=utf-8則表示編碼為utf-8。

    HTTP GET請求的格式:

    HTTP響應(yīng)的格式:

    ?自定義web框架

    經(jīng)過上面的學(xué)習(xí),那我們基于socket服務(wù)端的十幾行代碼寫一個我們自己的web框架。我們先不處理瀏覽器發(fā)送的請求,先讓瀏覽器能顯示我們web框架返回的信息,那我們就要按照HTTP協(xié)議的格式來發(fā)送響應(yīng)。

  • import?socket????
  • ????
  • sock?=?socket.socket(socket.AF_INET,?socket.SOCK_STREAM)????
  • sock.bind(('127.0.0.1',?8000))????
  • sock.listen()????
  • ????
  • while?True:????
  • ????conn,?addr?=?sock.accept()????
  • ????data?=?conn.recv(8096)????
  • ????#?給回復(fù)的消息加上響應(yīng)狀態(tài)行????
  • ????conn.send(b"HTTP/1.1?200?OK\r\n\r\n")????
  • ????conn.send(b"OK")????
  • ????conn.close()????
  • 我們通過十幾行代碼簡單地演示了web 框架的本質(zhì)。

    接下來就讓我們繼續(xù)完善我們的自定義web框架吧!

    根據(jù)不同的路徑返回不同的內(nèi)容

    這樣就結(jié)束了嗎? 如何讓我們的Web服務(wù)根據(jù)用戶請求的URL不同而返回不同的內(nèi)容呢??

    小事一樁,我們可以從請求相關(guān)數(shù)據(jù)里面拿到請求URL的路徑,然后拿路徑做一個判斷...

  • """?
  • 根據(jù)URL中不同的路徑返回不同的內(nèi)容?
  • """??
  • ??
  • import?socket??
  • ??
  • sk?=?socket.socket()??
  • sk.bind(("127.0.0.1",?8080))??#?綁定IP和端口??
  • sk.listen()??#?監(jiān)聽??
  • ??
  • while?True:??
  • ????#?等待連接??
  • ????conn,?add?=?sk.accept()??
  • ????data?=?conn.recv(8096)??#?接收客戶端發(fā)來的消息??
  • ????#?從data中取到路徑??
  • ????data?=?str(data,?encoding="utf8")??#?把收到的字節(jié)類型的數(shù)據(jù)轉(zhuǎn)換成字符串??
  • ????#?按\r\n分割??
  • ????data1?=?data.split("\r\n")[0]??
  • ????url?=?data1.split()[1]??#?url是我們從瀏覽器發(fā)過來的消息中分離出的訪問路徑??
  • ????conn.send(b'HTTP/1.1?200?OK\r\n\r\n')??#?因為要遵循HTTP協(xié)議,所以回復(fù)的消息也要加狀態(tài)行??
  • ????#?根據(jù)不同的路徑返回不同內(nèi)容??
  • ????if?url?==?"/index/":??
  • ????????response?=?b"index"??
  • ????elif?url?==?"/home/":??
  • ????????response?=?b"home"??
  • ????else:??
  • ????????response?=?b"404?not?found!"??
  • ??
  • ????conn.send(response)??
  • ????conn.close()??
  • 根據(jù)不同的路徑返回不同的內(nèi)容--函數(shù)版

    上面的代碼解決了不同URL路徑返回不同內(nèi)容的需求。

    我們返回的內(nèi)容是簡單的幾個字符,那如果我可以將返回的結(jié)果封裝成一個函數(shù)呢?

  • """?
  • 根據(jù)URL中不同的路徑返回不同的內(nèi)容--函數(shù)版?
  • """??
  • ??
  • import?socket??
  • ??
  • sk?=?socket.socket()??
  • sk.bind(("127.0.0.1",?8080))??#?綁定IP和端口??
  • sk.listen()??#?監(jiān)聽??
  • ??
  • ??
  • #?將返回不同的內(nèi)容部分封裝成函數(shù)??
  • def?func(url):??
  • ????s?=?"這是{}頁面!".format(url)??
  • ????return?bytes(s,?encoding="utf8")??
  • ??
  • ??
  • while?True:??
  • ????#?等待連接??
  • ????conn,?add?=?sk.accept()??
  • ????data?=?conn.recv(8096)??#?接收客戶端發(fā)來的消息??
  • ????#?從data中取到路徑??
  • ????data?=?str(data,?encoding="utf8")??#?把收到的字節(jié)類型的數(shù)據(jù)轉(zhuǎn)換成字符串??
  • ????#?按\r\n分割??
  • ????data1?=?data.split("\r\n")[0]??
  • ????url?=?data1.split()[1]??#?url是我們從瀏覽器發(fā)過來的消息中分離出的訪問路徑??
  • ????conn.send(b'HTTP/1.1?200?OK\r\n\r\n')??#?因為要遵循HTTP協(xié)議,所以回復(fù)的消息也要加狀態(tài)行??
  • ????#?根據(jù)不同的路徑返回不同內(nèi)容,response是具體的響應(yīng)體??
  • ????if?url?==?"/index/":??
  • ????????response?=?func(url)??
  • ????elif?url?==?"/home/":??
  • ????????response?=?func(url)??
  • ????else:??
  • ????????response?=?b"404?not?found!"??
  • ??
  • ????conn.send(response)??
  • ????conn.close()??
  • 根據(jù)不同的路徑返回不同的內(nèi)容--函數(shù)進(jìn)階版

    看起來上面的代碼寫了一個函數(shù),那肯定可以寫多個函數(shù),不同的路徑對應(yīng)執(zhí)行不同的函數(shù)拿到結(jié)果,但是我們要一個個判斷路徑,是不是很麻煩?我們有簡單的辦法來解決。

  • """?
  • 根據(jù)URL中不同的路徑返回不同的內(nèi)容--函數(shù)進(jìn)階版?
  • """??
  • ??
  • import?socket??
  • ??
  • sk?=?socket.socket()??
  • sk.bind(("127.0.0.1",?8080))??#?綁定IP和端口??
  • sk.listen()??#?監(jiān)聽??
  • ??
  • ??
  • #?將返回不同的內(nèi)容部分封裝成不同的函數(shù)??
  • def?index(url):??
  • ????s?=?"這是{}頁面XX!".format(url)??
  • ????return?bytes(s,?encoding="utf8")??
  • ??
  • ??
  • def?home(url):??
  • ????s?=?"這是{}頁面。。!".format(url)??
  • ????return?bytes(s,?encoding="utf8")??
  • ??
  • ??
  • #?定義一個url和實際要執(zhí)行的函數(shù)的對應(yīng)關(guān)系??
  • list1?=?[??
  • ????("/index/",?index),??
  • ????("/home/",?home),??
  • ]??
  • ??
  • while?True:??
  • ????#?等待連接??
  • ????conn,?add?=?sk.accept()??
  • ????data?=?conn.recv(8096)??#?接收客戶端發(fā)來的消息??
  • ????#?從data中取到路徑??
  • ????data?=?str(data,?encoding="utf8")??#?把收到的字節(jié)類型的數(shù)據(jù)轉(zhuǎn)換成字符串??
  • ????#?按\r\n分割??
  • ????data1?=?data.split("\r\n")[0]??
  • ????url?=?data1.split()[1]??#?url是我們從瀏覽器發(fā)過來的消息中分離出的訪問路徑??
  • ????conn.send(b'HTTP/1.1?200?OK\r\n\r\n')??#?因為要遵循HTTP協(xié)議,所以回復(fù)的消息也要加狀態(tài)行??
  • ????#?根據(jù)不同的路徑返回不同內(nèi)容??
  • ????func?=?None??#?定義一個保存將要執(zhí)行的函數(shù)名的變量??
  • ????for?item?in?list1:??
  • ????????if?item[0]?==?url:??
  • ????????????func?=?item[1]??
  • ????????????break??
  • ????if?func:??
  • ????????response?=?func(url)??
  • ????else:??
  • ????????response?=?b"404?not?found!"??
  • ??
  • ????#?返回具體的響應(yīng)消息??
  • ????conn.send(response)??
  • ????conn.close()??
  • 返回具體的HTML文件

    完美解決了不同URL返回不同內(nèi)容的問題。 但是我不想僅僅返回幾個字符串,我想給瀏覽器返回完整的HTML內(nèi)容,這又該怎么辦呢?

    沒問題,不管是什么內(nèi)容,最后都是轉(zhuǎn)換成字節(jié)數(shù)據(jù)發(fā)送出去的。 我們可以打開HTML文件,讀取出它內(nèi)部的二進(jìn)制數(shù)據(jù),然后再發(fā)送給瀏覽器。

  • """?
  • 根據(jù)URL中不同的路徑返回不同的內(nèi)容--函數(shù)進(jìn)階版?
  • 返回獨立的HTML頁面?
  • """??
  • ??
  • import?socket??
  • ??
  • sk?=?socket.socket()??
  • sk.bind(("127.0.0.1",?8080))??#?綁定IP和端口??
  • sk.listen()??#?監(jiān)聽??
  • ??
  • ??
  • #?將返回不同的內(nèi)容部分封裝成不同的函數(shù)??
  • def?index(url):??
  • ????#?讀取index.html頁面的內(nèi)容??
  • ????with?open("index.html",?"r",?encoding="utf8")?as?f:??
  • ????????s?=?f.read()??
  • ????#?返回字節(jié)數(shù)據(jù)??
  • ????return?bytes(s,?encoding="utf8")??
  • ??
  • ??
  • def?home(url):??
  • ????with?open("home.html",?"r",?encoding="utf8")?as?f:??
  • ????????s?=?f.read()??
  • ????return?bytes(s,?encoding="utf8")??
  • ??
  • ??
  • #?定義一個url和實際要執(zhí)行的函數(shù)的對應(yīng)關(guān)系??
  • list1?=?[??
  • ????("/index/",?index),??
  • ????("/home/",?home),??
  • ]??
  • ??
  • while?True:??
  • ????#?等待連接??
  • ????conn,?add?=?sk.accept()??
  • ????data?=?conn.recv(8096)??#?接收客戶端發(fā)來的消息??
  • ????#?從data中取到路徑??
  • ????data?=?str(data,?encoding="utf8")??#?把收到的字節(jié)類型的數(shù)據(jù)轉(zhuǎn)換成字符串??
  • ????#?按\r\n分割??
  • ????data1?=?data.split("\r\n")[0]??
  • ????url?=?data1.split()[1]??#?url是我們從瀏覽器發(fā)過來的消息中分離出的訪問路徑??
  • ????conn.send(b'HTTP/1.1?200?OK\r\n\r\n')??#?因為要遵循HTTP協(xié)議,所以回復(fù)的消息也要加狀態(tài)行??
  • ????#?根據(jù)不同的路徑返回不同內(nèi)容??
  • ????func?=?None??#?定義一個保存將要執(zhí)行的函數(shù)名的變量??
  • ????for?item?in?list1:??
  • ????????if?item[0]?==?url:??
  • ????????????func?=?item[1]??
  • ????????????break??
  • ????if?func:??
  • ????????response?=?func(url)??
  • ????else:??
  • ????????response?=?b"404?not?found!"??
  • ??
  • ????#?返回具體的響應(yīng)消息??
  • ????conn.send(response)??
  • ????conn.close()??
  • <!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><meta http-equiv="x-ua-compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1"><title>index</title> </head> <body> <div>這是index頁面</div> </body> </html> index.html <!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><meta http-equiv="x-ua-compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1"><title>index</title> </head> <body> <div>這是home頁面</div> </body> </html> home.html

    讓網(wǎng)頁動態(tài)起來

    這網(wǎng)頁能夠顯示出來了,但是都是靜態(tài)的啊。頁面的內(nèi)容都不會變化的,我想要的是動態(tài)網(wǎng)站。

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

  • """?
  • 根據(jù)URL中不同的路徑返回不同的內(nèi)容--函數(shù)進(jìn)階版?
  • 返回獨立的HTML頁面?
  • """??
  • ??
  • import?socket??
  • ??
  • sk?=?socket.socket()??
  • sk.bind(("127.0.0.1",?8080))??#?綁定IP和端口??
  • sk.listen()??#?監(jiān)聽??
  • ??
  • ??
  • #?將返回不同的內(nèi)容部分封裝成不同的函數(shù)??
  • def?index(url):??
  • ????#?讀取index.html頁面的內(nèi)容??
  • ????with?open("index.html",?"r",?encoding="utf8")?as?f:??
  • ????????s?=?f.read()??
  • ????#?返回字節(jié)數(shù)據(jù)??
  • ????return?bytes(s,?encoding="utf8")??
  • ??
  • ??
  • def?home(url):??
  • ????with?open("home.html",?"r",?encoding="utf8")?as?f:??
  • ????????s?=?f.read()??
  • ????return?bytes(s,?encoding="utf8")??
  • ??
  • ??
  • def?timer(url):??
  • ????import?time??
  • ????with?open("time.html",?"r",?encoding="utf8")?as?f:??
  • ????????s?=?f.read()??
  • ????????s?=?s.replace('@@time@@',?time.strftime("%Y-%m-%d?%H:%M:%S"))??
  • ????return?bytes(s,?encoding="utf8")??
  • ??
  • ??
  • #?定義一個url和實際要執(zhí)行的函數(shù)的對應(yīng)關(guān)系??
  • list1?=?[??
  • ????("/index/",?index),??
  • ????("/home/",?home),??
  • ????("/time/",?timer),??
  • ]??
  • ??
  • while?True:??
  • ????#?等待連接??
  • ????conn,?add?=?sk.accept()??
  • ????data?=?conn.recv(8096)??#?接收客戶端發(fā)來的消息??
  • ????#?從data中取到路徑??
  • ????data?=?str(data,?encoding="utf8")??#?把收到的字節(jié)類型的數(shù)據(jù)轉(zhuǎn)換成字符串??
  • ????#?按\r\n分割??
  • ????data1?=?data.split("\r\n")[0]??
  • ????url?=?data1.split()[1]??#?url是我們從瀏覽器發(fā)過來的消息中分離出的訪問路徑??
  • ????conn.send(b'HTTP/1.1?200?OK\r\n\r\n')??#?因為要遵循HTTP協(xié)議,所以回復(fù)的消息也要加狀態(tài)行??
  • ????#?根據(jù)不同的路徑返回不同內(nèi)容??
  • ????func?=?None??#?定義一個保存將要執(zhí)行的函數(shù)名的變量??
  • ????for?item?in?list1:??
  • ????????if?item[0]?==?url:??
  • ????????????func?=?item[1]??
  • ????????????break??
  • ????if?func:??
  • ????????response?=?func(url)??
  • ????else:??
  • ????????response?=?b"404?not?found!"??
  • ??
  • ????#?返回具體的響應(yīng)消息??
  • ????conn.send(response)??
  • ????conn.close()??
  • 服務(wù)器程序和應(yīng)用程序

    對于真實開發(fā)中的python web程序來說,一般會分為兩部分:服務(wù)器程序和應(yīng)用程序。

    服務(wù)器程序負(fù)責(zé)對socket服務(wù)端進(jìn)行封裝,并在請求到來時,對請求的各種數(shù)據(jù)進(jìn)行整理。

    應(yīng)用程序則負(fù)責(zé)具體的邏輯處理。為了方便應(yīng)用程序的開發(fā),就出現(xiàn)了眾多的Web框架,例如:Django、Flask、web.py 等。不同的框架有不同的開發(fā)方式,但是無論如何,開發(fā)出的應(yīng)用程序都要和服務(wù)器程序配合,才能為用戶提供服務(wù)。

    ?

    這樣,服務(wù)器程序就需要為不同的框架提供不同的支持。這樣混亂的局面無論對于服務(wù)器還是框架,都是不好的。對服務(wù)器來說,需要支持各種不同框架,對框架來說,只有支持它的服務(wù)器才能被開發(fā)出的應(yīng)用使用。

    這時候,標(biāo)準(zhǔn)化就變得尤為重要。我們可以設(shè)立一個標(biāo)準(zhǔn),只要服務(wù)器程序支持這個標(biāo)準(zhǔn),框架也支持這個標(biāo)準(zhǔn),那么他們就可以配合使用。一旦標(biāo)準(zhǔn)確定,雙方各自實現(xiàn)。這樣,服務(wù)器可以支持更多支持標(biāo)準(zhǔn)的框架,框架也可以使用更多支持標(biāo)準(zhǔn)的服務(wù)器。

    WSGI(Web Server Gateway Interface)就是一種規(guī)范,它定義了使用Python編寫的web應(yīng)用程序與web服務(wù)器程序之間的接口格式,實現(xiàn)web應(yīng)用程序與web服務(wù)器程序間的解耦。

    常用的WSGI服務(wù)器有uwsgi、Gunicorn。而Python標(biāo)準(zhǔn)庫提供的獨立WSGI服務(wù)器叫wsgiref,Django開發(fā)環(huán)境用的就是這個模塊來做服務(wù)器。

    ?

    從這繼續(xù)...

    wsgiref

    我們利用wsgiref模塊來替換我們自己寫的web框架的socket server部分:

  • """??
  • 根據(jù)URL中不同的路徑返回不同的內(nèi)容--函數(shù)進(jìn)階版??
  • 返回HTML頁面??
  • 讓網(wǎng)頁動態(tài)起來??
  • wsgiref模塊版??
  • """???
  • ?????
  • from?wsgiref.simple_server?import?make_server???
  • ?????
  • ?????
  • #?將返回不同的內(nèi)容部分封裝成函數(shù)???
  • def?index(url):???
  • ????#?讀取index.html頁面的內(nèi)容???
  • ????with?open("index.html",?"r",?encoding="utf8")?as?f:???
  • ????????s?=?f.read()???
  • ????#?返回字節(jié)數(shù)據(jù)???
  • ????return?bytes(s,?encoding="utf8")???
  • ?????
  • ?????
  • def?home(url):???
  • ????with?open("home.html",?"r",?encoding="utf8")?as?f:???
  • ????????s?=?f.read()???
  • ????return?bytes(s,?encoding="utf8")???
  • ?????
  • ?????
  • def?timer(url):???
  • ????import?time???
  • ????with?open("time.html",?"r",?encoding="utf8")?as?f:???
  • ????????s?=?f.read()???
  • ????????s?=?s.replace('@@time@@',?time.strftime("%Y-%m-%d?%H:%M:%S"))???
  • ????return?bytes(s,?encoding="utf8")???
  • ?????
  • ?????
  • #?定義一個url和實際要執(zhí)行的函數(shù)的對應(yīng)關(guān)系???
  • list1?=?[???
  • ????("/index/",?index),???
  • ????("/home/",?home),???
  • ????("/time/",?timer),???
  • ]???
  • ?????
  • ?????
  • def?run_server(environ,?start_response):???
  • ????start_response('200?OK',?[('Content-Type',?'text/html;charset=utf8'),?])??#?設(shè)置HTTP響應(yīng)的狀態(tài)碼和頭信息???
  • ????url?=?environ['PATH_INFO']??#?取到用戶輸入的url???
  • ????func?=?None???
  • ????for?i?in?list1:???
  • ????????if?i[0]?==?url:???
  • ????????????func?=?i[1]???
  • ????????????break???
  • ????if?func:???
  • ????????response?=?func(url)???
  • ????else:???
  • ????????response?=?b"404?not?found!"???
  • ????return?[response,?]???
  • ?????
  • ?????
  • if?__name__?==?'__main__':???
  • ????httpd?=?make_server('127.0.0.1',?8090,?run_server)???
  • ????print("我在8090等你哦...")???
  • ????httpd.serve_forever()??
  • jinja2

    上面的代碼實現(xiàn)了一個簡單的動態(tài),我完全可以從數(shù)據(jù)庫中查詢數(shù)據(jù),然后去替換我html中的對應(yīng)內(nèi)容,然后再發(fā)送給瀏覽器完成渲染。 這個過程就相當(dāng)于HTML模板渲染數(shù)據(jù)。 本質(zhì)上就是HTML內(nèi)容中利用一些特殊的符號來替換要展示的數(shù)據(jù)。 我這里用的特殊符號是我定義的,其實模板渲染有個現(xiàn)成的工具: jinja2

    下載jinja2:

    pip install jinja2 <!DOCTYPE html> <html lang="zh-CN"> <head><meta charset="UTF-8"><meta http-equiv="x-ua-compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1"><title>Title</title> </head> <body><h1>姓名:{{name}}</h1><h1>愛好:</h1><ul>{% for hobby in hobby_list %}<li>{{hobby}}</li>{% endfor %}</ul> </body> </html> index2.html文件

    使用jinja2渲染index2.html文件:

  • from?wsgiref.simple_server?import?make_server??
  • from?jinja2?import?Template??
  • ??
  • ??
  • def?index(url):??
  • ????#?讀取HTML文件內(nèi)容??
  • ????with?open("index2.html",?"r",?encoding="utf8")?as?f:??
  • ????????data?=?f.read()??
  • ????????template?=?Template(data)???#?生成模板文件??
  • ????????ret?=?template.render({'name':?'alex',?'hobby_list':?['抽煙',?'喝酒',?'燙頭']})???#?把數(shù)據(jù)填充到模板中??
  • ????return?bytes(ret,?encoding="utf8")??
  • ??
  • ??
  • def?home(url):??
  • ????with?open("home.html",?"r",?encoding="utf8")?as?f:??
  • ????????s?=?f.read()??
  • ????return?bytes(s,?encoding="utf8")??
  • ??
  • ??
  • #?定義一個url和實際要執(zhí)行的函數(shù)的對應(yīng)關(guān)系??
  • list1?=?[??
  • ????("/index/",?index),??
  • ????("/home/",?home),??
  • ]??
  • ??
  • ??
  • def?run_server(environ,?start_response):??
  • ????start_response('200?OK',?[('Content-Type',?'text/html;charset=utf8'),?])??#?設(shè)置HTTP響應(yīng)的狀態(tài)碼和頭信息??
  • ????url?=?environ['PATH_INFO']??#?取到用戶輸入的url??
  • ????func?=?None??
  • ????for?i?in?list1:??
  • ????????if?i[0]?==?url:??
  • ????????????func?=?i[1]??
  • ????????????break??
  • ????if?func:??
  • ????????response?=?func(url)??
  • ????else:??
  • ????????response?=?b"404?not?found!"??
  • ????return?[response,?]??
  • ??
  • ??
  • if?__name__?==?'__main__':??
  • ????httpd?=?make_server('127.0.0.1',?8090,?run_server)??
  • ????print("我在8090等你哦...")??
  • ????httpd.serve_forever()??
  • 現(xiàn)在的數(shù)據(jù)是我們自己手寫的,那可不可以從數(shù)據(jù)庫中查詢數(shù)據(jù),來填充頁面呢?

    使用pymysql連接數(shù)據(jù)庫:

    conn = pymysql.connect(host="127.0.0.1", port=3306, user="root", passwd="xxx", db="xxx", charset="utf8") cursor = conn.cursor(cursor=pymysql.cursors.DictCursor) cursor.execute("select name, age, department_id from userinfo") user_list = cursor.fetchall() cursor.close() conn.close()

    創(chuàng)建一個測試的user表:

    CREATE TABLE user(id int auto_increment PRIMARY KEY,name CHAR(10) NOT NULL,hobby CHAR(20) NOT NULL )engine=innodb DEFAULT charset=UTF8;

    模板的原理就是字符串替換,我們只要在HTML頁面中遵循jinja2的語法規(guī)則寫上,其內(nèi)部就會按照指定的語法進(jìn)行相應(yīng)的替換,從而達(dá)到動態(tài)的返回內(nèi)容。

    Django

    Django官網(wǎng)下載頁面

    安裝(安裝最新LTS版):

    pip3 install django==1.11.15

    創(chuàng)建一個django項目:

    下面的命令創(chuàng)建了一個名為"mysite"的Django 項目:

    django-admin startproject mysite

    目錄介紹:

    mysite/ ├── manage.py # 管理文件 └── mysite # 項目目錄├── __init__.py├── settings.py # 配置├── urls.py # 路由 --> URL和函數(shù)的對應(yīng)關(guān)系└── wsgi.py # runserver命令就使用wsgiref模塊做簡單的web server

    運行Django項目:

    python manage.py runserver 127.0.0.1:8000

    模板文件配置:

    TEMPLATES = [{'BACKEND': 'django.template.backends.django.DjangoTemplates','DIRS': [os.path.join(BASE_DIR, "template")], # template文件夾位置'APP_DIRS': True,'OPTIONS': {'context_processors': ['django.template.context_processors.debug','django.template.context_processors.request','django.contrib.auth.context_processors.auth','django.contrib.messages.context_processors.messages',],},}, ]

    靜態(tài)文件配置:

    STATIC_URL = '/static/' # HTML中使用的靜態(tài)文件夾前綴 STATICFILES_DIRS = [os.path.join(BASE_DIR, "static"), # 靜態(tài)文件存放位置 ]

    看不明白?有圖有真相:

    剛開始學(xué)習(xí)時可在配置文件中暫時禁用csrf中間件,方便表單提交測試。

    MIDDLEWARE = ['django.middleware.security.SecurityMiddleware','django.contrib.sessions.middleware.SessionMiddleware','django.middleware.common.CommonMiddleware',# 'django.middleware.csrf.CsrfViewMiddleware','django.contrib.auth.middleware.AuthenticationMiddleware','django.contrib.messages.middleware.MessageMiddleware','django.middleware.clickjacking.XFrameOptionsMiddleware', ]

    Django基礎(chǔ)必備三件套:

    from django.shortcuts import HttpResponse, render, redirect

    HttpResponse

    內(nèi)部傳入一個字符串參數(shù),返回給瀏覽器。

    例如:

    def index(request):# 業(yè)務(wù)邏輯代碼return HttpResponse("OK")

    render

    除request參數(shù)外還接受一個待渲染的模板文件和一個保存具體數(shù)據(jù)的字典參數(shù)。

    將數(shù)據(jù)填充進(jìn)模板文件,最后把結(jié)果返回給瀏覽器。(類似于我們上面用到的jinja2)

    例如:

    def index(request):# 業(yè)務(wù)邏輯代碼return render(request, "index.html", {"name": "alex", "hobby": ["燙頭", "泡吧"]})

    redirect

    接受一個URL參數(shù),表示跳轉(zhuǎn)到指定的URL。

    例如:

    def index(request):# 業(yè)務(wù)邏輯代碼return redirect("/home/")

    重定向是怎么回事?

    啟動Django報錯:

    Django 啟動時報錯 UnicodeEncodeError ...

    報這個錯誤通常是因為計算機(jī)名為中文,改成英文的計算機(jī)名重啟下電腦就可以了。

    轉(zhuǎn)載于:https://www.cnblogs.com/jiadi321/p/9669646.html

    總結(jié)

    以上是生活随笔為你收集整理的Web框架的原理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。