Django框架原理
一、HTTP協議
1、什麽是HTTP協議
協議就是約束雙方的一個準則HTTP,超文本傳輸協議(HyperText Transfer Protocol) 是互聯網上應用最為廣泛的一種網絡協議 所有的WWW文件都必須遵守這個標準 設計HTTP最初的目的是為了提供一種發布和接收HTML頁面的方法、約束請求與響應的規則。
2、HTTP的組成部分
(1)請求
(2)響應
請求和響應都是成對存在的。
3、請求的發送方式
- 通過瀏覽器的地址欄
- 通過html當中的form表單
- 通過a鏈接的href
- src屬性
4、請求部分
瀏覽器====>服務器
原始的請求部分
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‘
按換行分隔後的請求部分
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
(1)請求行
請求行以一個方法符號開頭,以空格分開,後面跟著請求的URI和協議的版本,其中 Method表示請求方法;Request-URI是一個統一資源標識符;HTTP-Version表示請求的HTTP協議版本;
GET / HTTP/1.1\r\n
請求方式:POST、GET 請求的資源:/Myxq/login.html?username=myxq&pwd=1234 協議版本: HTTP/1.0 發送請求,創建一次連接 獲得一個web資源,連接斷開 HTTP/1.1 發送請求,創建一次連接 獲得多個web資源,保持連接
(2)請求頭
請求頭是客戶端發送給服務器端的一些信息,使用鍵值對表示key:value
常見請求頭`Referer`: 瀏覽器通知服務器,當前請求來自何處。 如果是直接訪問 則不會有這個頭。常用於:防盜鏈 `If-Modified-Since`: 瀏覽器通知服務器 本地緩存的最後變更時間。 `Cookie`:用於存放瀏覽器緩存的cookie信息。 `User-Agent`: 瀏覽器通知服務器 客戶端瀏覽器與操作系統相關信息 `Connection`: 保持連接狀態。Keep-Alive 連接中,close 已關閉 `Host`:請求的服務器主機名 `Content-Length`:請求體的長度 `Content-Type`:如果是POST請求,會有這個頭, 默認值為`application/x-www-form-urlencoded`, 表示請求體內容使用url編碼 `Accept`: 瀏覽器可支持的MIME類型。 文件類型的一種描述方式`text/html` , `html`文件 `text/css`, `css`文件`text/javascript`, `js`文件`image`,所有圖片文件 `Accept-Encoding`: 瀏覽器通知服務器 瀏覽器支持的數據壓縮格式。 如:GZIP壓縮 `Accept-Language`: 瀏覽器通知服務器,瀏覽器支持的語言 自動的把客戶端的信息發送給服務器
(3)請求體
當請求方式是post的時,請求體會有請求的參數,如果請求方式為get,那麽請求參數不會出現在請求體中,會拼接在url地址後面。
小結:
格式:
請求方式 URL 協議版本\r\n
k1: v1\r\n
k2:v2\r\n
\r\n
請求體(請求數據) 可以有可以沒有 GET請求沒有請求數據
5、響應部分
服務器=====>瀏覽器
點擊view source之後顯示如下圖
(1)響應行
服務器響應給客戶端瀏覽器的狀態碼,根據不同的狀態碼,可以看出此次請求的結果如何
Http協議狀態碼 `200` :請求成功 302 :請求重定向 304 :請求資源沒有改變,訪問本地緩存。 404 :請求資源不存在。 通常是用戶路徑編寫錯誤, 也可能是服務器資源已刪除。 500 :服務器內部錯誤。通常程序拋異常。 其它狀態碼 成功 200 OK 201 已創建 202 接收 203 非認證信息 204 無內容 205 重置內容 206 部分內容 重定向 300 多路選擇 301 永久轉移 302 暫時轉移 303 參見其它 304 未修改(`Not Modified`) 305 使用代理 客戶方錯誤 400 錯誤請求(`Bad Request`) 401 未認證 402 需要付費 403 禁止(`Forbidden`) 404 未找到(`Not Found`) 405 方法不允許 406 不接受 407 需要代理認證 408 請求超時 409 沖突 410 失敗 411 需要長度 412 條件失敗 413 請求實體太大 414 請求URI太長 415 不支持媒體類型 服務器錯誤 500 服務器內部錯誤 501 未實現(`Not Implemented`) 502 網關失敗 504 網關超時
(2)響應頭
服務器端將信息,以鍵值對的形式返回給客戶端
常見響應頭 `Location`: 指定響應的路徑 需要與狀態碼302配合使用,完成跳轉 `Content-Type`: 響應正文的類型(MIME類型) `Content-Disposition`: 通過瀏覽器以下載方式解析正文 `Set-Cookie`: 服務器向瀏覽器寫入cookie `Content-Encoding`: 服務器使用的壓縮格式 `Content-length`: 響應正文的長度 `Refresh`:定時刷新 `Server`: 服務器名稱,默認值:`Apache-Coyote/1.1`。 可以通過conf/server.xml配置進行修改 `Last-Modified`: 服務器通知瀏覽器,文件的最後修改時間 自動的把服務器端的信息傳給客戶端
(3)響應體
響應體是服務器回寫給客戶端的頁面正文,瀏覽器將正文加載到內存,然後解析渲染顯示頁面內容
小結:
格式:
“協議版本 狀態碼 狀態描述符\r\n
k1: v1\r\n
k2:v2\r\n
Content-Type:text/html;charset=utf-8\r\n
\r\n
響應體 (HTML代碼)”
二、web框架的本質
WSGI(Web Server Gateway Interface)規範,它定義了使用Python編寫的web應用程序與web服務器程序之間的接口格式,實現web應用程序與web服務器程序間的解耦。常用的WSGI服務器有uwsgi、Gunicorn。而Python標準庫提供的獨立WSGI服務器叫wsgiref,Django開發環境用的就是這個模塊來做服務器。
所有的Web應用本質上就是一個socket服務端,而用戶的瀏覽器就是一個socket客戶端。
1、自定義一個web框架
最簡單的
# 導入模塊 import socket # 創建一個socket對象 sk = socket.socket() # 綁定IP和端口 sk.bind((‘127.0.0.1‘,8000)) # 監聽 sk.listen() while True: # 等待連接 conn, addr = sk.accept() # 接收消息 data = conn.recv(8000) print(data) # 發送消息 conn.send(b‘HTTP/1.1 200 OK\r\n\r\n‘) conn.send(b‘OK‘) # 關閉連接 conn.close()
2、根據不同的路徑返回不同的內容
# 導入模塊
import socket
# 創建一個socket對象
sk = socket.socket()
# 綁定IP和端口
sk.bind((‘127.0.0.1‘,8000))
# 監聽
sk.listen()
while True:
# 等待連接
conn, addr = sk.accept()
# 接收消息
data = conn.recv(8000)
url = data.decode(‘utf-8‘).split()[1]
# 發送消息
conn.send(b‘HTTP/1.1 200 OK\r\n\r\n‘)
if url == ‘/oumei/‘:
conn.send(b‘welcome to oumei bankuai‘)
elif url == ‘/rihan/‘:
conn.send(b‘welcome to rihan bankuai‘)
else:
conn.send(b‘luzhizhong‘)
# 關閉連接
conn.close()
3、根據不同的路徑返回不同的內容--函數版
# 導入模塊
import socket
# 創建一個socket對象
sk = socket.socket()
# 綁定IP和端口
sk.bind((‘127.0.0.1‘,8000))
# 監聽
sk.listen()
def oumei(url):
return b‘welcome to oumei bankuai‘
def rihan(url):
return b‘welcome to rihan bankuai‘
while True:
# 等待連接
conn, addr = sk.accept()
# 接收消息
data = conn.recv(8000)
url = data.decode(‘utf-8‘).split()[1]
# 發送消息
conn.send(b‘HTTP/1.1 200 OK\r\n\r\n‘)
if url == ‘/oumei/‘:
response = oumei(url)
elif url == ‘/rihan/‘:
response = rihan(url)
else:
response = b‘luzhizhong‘
# 返回信息
conn.send(response)
# 關閉連接
conn.close()
4、根據不同的路徑返回不同的內容--函數進階版
# 導入模塊
import socket
# 創建一個socket對象
sk = socket.socket()
# 綁定IP和端口
sk.bind((‘127.0.0.1‘,8000))
# 監聽
sk.listen()
def oumei(url):
return b‘welcome to oumei bankuai‘
def rihan(url):
return b‘welcome to rihan bankuai‘
def dongnanya(url):
return b‘welcome to dongnanya bankuai‘
# 定義一個URL和函數的對應函數
list1 = [
(‘/oumei/‘,oumei),
(‘/rihan/‘,rihan),
(‘/dongnanya/‘,dongnanya),
]
while True:
# 等待連接
conn, addr = sk.accept()
# 接收消息
data = conn.recv(8000)
url = data.decode(‘utf-8‘).split()[1]
# 發送消息
conn.send(b‘HTTP/1.1 200 OK\r\n\r\n‘)
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‘
# 返回信息
conn.send(response)
# 關閉連接
conn.close()
5、返回具體的HTML文件
服務端:
# 導入模塊
import socket
# 創建一個socket對象
sk = socket.socket()
# 綁定IP和端口
sk.bind((‘127.0.0.1‘,8000))
# 監聽
sk.listen()
def timer(url):
import time
with open(‘time.html‘,‘r‘,encoding=‘utf-8‘) as f:
ret = f.read()
ret = ret.replace(‘@@time@@‘,time.strftime("%Y-%m-%d %H:%M:%S"))
return ret.encode(‘utf-8‘)
def index(url):
with open(‘index.html‘,‘rb‘) as f:
return f.read()
def oumei(url):
return b‘welcome to oumei bankuai‘
def rihan(url):
return b‘welcome to rihan bankuai‘
def dongnanya(url):
return b‘welcome to dongnanya bankuai‘
# 定義一個URL和函數的對應函數
list1 = [
(‘/oumei/‘,oumei),
(‘/rihan/‘,rihan),
(‘/dongnanya/‘,dongnanya),
(‘/index/‘,index),
(‘/time/‘,timer),
]
while True:
# 等待連接
conn, addr = sk.accept()
# 接收消息
data = conn.recv(8000)
url = data.decode(‘utf-8‘).split()[1]
# 發送消息
conn.send(b‘HTTP/1.1 200 OK\r\n\r\n‘)
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‘
# 返回信息
conn.send(response)
# 關閉連接
conn.close()
index網頁頁面:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>index</title>
</head>
<body>
<h1>這是index頁面</h1>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>當前時間是:@@time@@</h1>
</body>
</html>
6、wsgi版
from wsgiref.simple_server import make_server # 將返回不同的內容部分封裝成函數 def index(url): # 讀取index.html頁面的內容 with open("index.html", "r", encoding="utf8") as f: s = f.read() # 返回字節數據 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和實際要執行的函數的對應關系 list1 = [ ("/index/", index), ("/home/", home), ("/time/", timer), ] def run_server(environ, start_response): start_response(‘200 OK‘, [(‘Content-Type‘, ‘text/html;charset=utf8‘), ]) # 設置HTTP響應的狀態碼和頭信息 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()
7、jinja2渲染版
from wsgiref.simple_server import make_server from jinja2 import Template def index(url): # 讀取HTML文件內容 with open("index2.html", "r", encoding="utf8") as f: data = f.read() template = Template(data) # 生成模板文件 ret = template.render({‘name‘: ‘egon‘, ‘hobby_list‘: [‘街舞‘, ‘喝酒‘, ‘燙頭‘]}) # 把數據填充到模板中 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和實際要執行的函數的對應關系 list1 = [ ("/index/", index), ("/home/", home), ] def run_server(environ, start_response): start_response(‘200 OK‘, [(‘Content-Type‘, ‘text/html;charset=utf8‘), ]) # 設置HTTP響應的狀態碼和頭信息 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()
小結:
web框架
本質: socket服務端
功能:
a. socket收發消息
b. URL和函數的對應關系,根據不同的URL執行不同的函數,返回函數的結果
c. 讀取HTML文件,進行了一個字符替換(模板渲染)
分類:
Django flask tornado
完成了a,b,c三個功能的 ——》 tornado
完成了b,c 兩個功能 ——》 Django
完成了b 一個功能 ——》 flask
另一種分類:
1. Django 大而全
2. 其他 短小精悍
Django框架原理