文章目錄
- 一.Web應用本質
- 1.socket本質
- 二.發送HTTP協議、回應
- 1.HTTP協議
- 2.HTTP發送回應
- ? 靜態網頁
- ? 動態網頁
- 三.jinja2模板渲染
一.Web應用本質
為了了解Django的客戶端與服務端的互動原理,我們需要了解Web應用的本質方便以后更加的理解Django原理
在Web應用中,服務器把網頁傳給瀏覽器,實際上就是把網頁的HTML代碼發送給瀏覽器,讓瀏覽器顯示出來,而瀏覽器和服務器之間的傳輸協議是HTTP,所以本質上就是:
- 瀏覽器發送一個HTTP請求
- 服務器收到請求,生成一個HTML檔案
- 服務器把HTML檔案作為HTTP回應的Body發送給瀏覽器
- 瀏覽器收到HTTP回應,從HTTP Body取出HTML檔案并顯示,
而在http協議中,我們可以用socket來實作
1.socket本質
web服務器本質上可以認為是一段代碼,可以不斷的處理http協議的網路請求,而http協議可以使用socket實作,并且http協議是一個無狀態的協議,即瀏覽器發起請求,服務器接收請求,然后給瀏覽器回復資料,然后斷開連接;那么可以用socket來實作一個最簡單的web服務器,
- HTTP:
無狀態、短連接 - TCP:
不斷開(socket連接) - 服務器把HTML檔案作為HTTP回應的Body發送給瀏覽器
- WEB應用(網站)
- 瀏覽器(socket客服端)
在客戶端訪問網址后,通過ping 網址 轉化為ip地址進行訪問(默認監聽80埠) - 瀏覽器(socket服務端)
1. 監聽ip和埠
2. 收到客戶端傳來的資料
3. 回應客戶端,發送回應資料
4. 斷開客戶端連接
- 瀏覽器(socket客服端)
示意如下:
import socket
sock = socket.socket()
# 網頁和監聽埠(這里先默認本機埠的8080)
sock.bind(('127.0.0.1',8080))
# 等待五個用戶
sock.listen(5)
# 因為socket是TCP協議不斷開
while True:
#等待
conn,addr= sock.accept()
# 有人來連接
# 獲取用戶發送的資料
data = conn.recv(8096)
# 回資料,轉位元組
conn.send(b'123123')
# 斷開客戶端連接
conn.close()
訪問127.0.0.1:8080
示意如下:

二.發送HTTP協議、回應
1.HTTP協議
在html中發送獲取資料都遵從著HTTP協議,隨著時間的變化一點一點的在改變,直到現在,我們可以通過socket發來的請求列印data來查看http發送的回應資料:
print(data)
#回傳
"""
回應頭
狀態:200 OK
'GET /(get請求資料默認放這) HTTP/1.1' \
'Host: 127.0.0.1:8080' \
'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:84.0) Gecko/20100101 Firefox/84.0' \
'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8' \
'Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2' \
'Accept-Encoding: gzip, deflate' \
'Connection: keep-alive' \
'Upgrade-Insecure-Requests: 1'
請求體:如果是post請求資料放這
回應體
'GET /favicon.ico HTTP/1.1' \
'Host: 127.0.0.1:8080' \
'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:84.0) Gecko/20100101 Firefox/84.0' \
'Accept: image/webp,*/*' \
'Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2' \
'Accept-Encoding: gzip, deflate' \
'Connection: keep-alive' \
'Referer: http://127.0.0.1:8080/'
"""
很顯然我們上面寫的代碼沒有根據HTTP的協議發送請求,應該遵循HTTP協議,用戶在頁面上看到的內容”字串“(看到頁面效果,是由瀏覽器決議出來的)
示意如下:
import socket
sock = socket.socket()
# 網頁和監聽埠(這里先默認本機埠的8080)
sock.bind(('127.0.0.1',8080))
# 等待五個用戶
sock.listen(5)
# 因為socket是TCP協議不斷開
while True:
#等待
conn,addr= sock.accept()
# 有人來連接
#1. 獲取用戶發送的資料
data = conn.recv(8096)
# 4. 遵循http協議
conn.send(b'HTTP/1.1 200 OK\r\n\r\n')
# 5. 回傳資料,轉位元組
conn.send(b'123123')
# 6. 斷開客戶端連接
conn.close()
2.HTTP發送回應
但是我們目前怎么樣都只有一個頁面,不能像其他的網站一樣有多個頁面,所以我們要從客戶端發來的請求中拿到資料在做出回應在回傳:
- 獲取用戶發送的資料
- 接收請求傳來的資料
- 分割資料
- 遵循http協議
- 回傳相應資料,轉位元組
- 斷開客戶端連接
示意如下:
import socket
# 相應資料函式
def f1(request):
return b"f1"
def f2(request):
return b"f2"
# 類似django的路由
routers = [
('/xxx', f1),
('/ooo', f2),
]
# 主函式
def run():
sock = socket.socket()
# 網頁和監聽埠(這里先默認本機埠的8080)
sock.bind(('127.0.0.1', 8080))
# 等待五個用戶
sock.listen(5)
# 因為socket是TCP協議不斷開
while True:
# 等待
conn, addr = sock.accept()
# 有人來連接
# 1. 獲取用戶發送的資料
data = conn.recv(8096)
# 2. 接收請求傳來的資料
data = str(data, encoding='utf-8')
# 3. 分割資料,請求頭,和請求體
headers, bodys = data.split('\r\n\r\n')
temp_list = headers.split('\r\n')
# 獲取一.get請求 二.訪問url 三.請求體
res, url, hea = temp_list[0].split(' ')
func_name = None
# 4. 遵循http協議
conn.send(b'HTTP/1.1 200 OK\r\n\r\n')
# 這時候列印的就是后綴的值
print(url)
# 遍歷路由
for item in routers:
if item[0] == url:
# 判斷是否是該url回傳函式地址
func_name = item[1]
break
# 判斷函式是否存在
if func_name:
response = func_name(data)
else:
response = b"404 not found"
# 5. 回傳相應資料,轉位元組
conn.send(response)
# 6. 斷開客戶端連接
conn.close()
if __name__ == '__main__':
run()
訪問127.0.0.1:8080
示意如下:

所以通過上面例子我們可以寫一個html然后渲染,
? 靜態網頁
因為如果想要資料庫實時動態產生,我們需要資料庫的幫助,不過在開始介紹前,我們先寫靜態網頁,首先在當前主目錄下創建兩個檔案夾動態網頁和靜態網頁,用于區分一下動態網頁和靜態網頁,
在靜態檔案/index.html中定義視圖如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<table border="1">
<thead>
<tr>
<th>ID</th>
<th>用戶名</th>
<th>郵箱</th>
</tr>
</thead>
<tbody>
<tr>
<th>1</th>
<th>root</th>
<th>root@qq.com</th>
</tr>
</tbody>
</table>
</body>
</html>
在f1中定義函式如下:
def f1(request)
f=open('靜態網頁/index.html', 'rb')
data=f.read()
f.close()
return data
訪問127.0.0.1:8080/xxx
示意如下:

? 動態網頁
在前面的文章有寫到操作資料庫的方法,種類很多這里我們使用pymysql來實作,如果沒看到這里的可以點開鏈接:點我!!!進行查看,
- 匯入驅動程式
pip install pymysql
- 創建資料庫連接
import pymysql
# 創建連接
conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='xxx', db='myemployees')
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
# 關閉
cursor.close()
conn.close()
在這里我們把/ooo改成/userlist.htm為了告訴大家django的路由和這個很像
routers = [
('/xxx', f1),
('/userlist.htm', f2),
]
在動態網頁/userlist.html中定義視圖如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<table border="1">
<thead>
<tr>
<th>ID</th>
<th>用戶名</th>
<th>郵箱</th>
</tr>
</thead>
<tbody>
<tr>
@@content@@
</tr>
</tbody>
</table>
</body>
</html>
因為回傳的是字串所以我們可以對字串進行拼接成html看得懂的,然后在從html轉化為位元組傳給瀏覽器,
在f2中定義函式如下:
def f2(request):
# 創建連接
conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='123456', db='myemployees')
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
# 在資料庫查詢資料
cursor.execute("SELECT employee_id,first_name,phone_number FROM employees")
# 獲取所有查到的資料
user_list = cursor.fetchall()
# 關閉
cursor.close()
conn.close()
# 拼接
content_list = []
for row in user_list:
tp = "<tr><td>%s</td><td>%s</td><td>%s</td></tr>" % (row['employee_id'], row['first_name'], row['phone_number'])
# 添加進來
content_list.append(tp)
# 拼接到一起
content = "".join(content_list)
# 打開userlist.html
f = open('動態網頁/userlist.html', 'r', encoding='utf-8')
# 獲得字串(模板的渲染)
template = f.read()
# 關閉
f.close()
# 把我之前在html寫的@@content@@修改
data = template.replace('@@content@@', content)
# 把寫好的字串轉成位元組的形式給瀏覽器渲染成可視化界面
return bytes(data, encoding='utf-8')
此時訪問127.0.0.1:8080/userlist.htm
示意如下:

三.jinja2模板渲染
對于上面的一系列操作,相對麻煩,又要替換字串,轉位元組,還得自己拼接,這時python中就出來了一個模塊jinjia2,它可以幫助我們進行模板的渲染,而渲染的規則要和它一致,
- 安裝jinja2模塊
pip install jinja2
- 添加一個路由和函式
routers = [
('/xxx', f1),
('/userlist.htm', f2),
('/host.html',f3),
]
在動態網頁/host.html中定義視圖如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<table border="1">
<thead>
<tr>
<th>ID</th>
<th>用戶名</th>
<th>郵箱</th>
</tr>
</thead>
<tbody>
{% for row in user_list %}
<tr>
<td>{{ row.employee_id }}</td>
<td>{{ row.first_name }}</td>
<td>{{ row.phone_number }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</body>
</html>
在f3中定義函式如下:
from jinja2 import Template
def f3(request):
f = open('動態網頁/host.html', 'r', encoding='utf-8')
# 獲得字串(模板的渲染)
data = f.read()
# 關閉
f.close()
# 創建連接
conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='123456', db='myemployees')
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
# 在資料庫查詢資料
cursor.execute("SELECT employee_id,first_name,phone_number FROM employees")
# 獲取所有查到的資料
user_list = cursor.fetchall()
# 關閉
cursor.close()
conn.close()
# 使用jinja2渲染
template=Template(data)
data=template.render(user_list=user_list)
# 上傳
return data.encode('utf-8')
此時訪問此時訪問127.0.0.1:8080/host.html
示意如下:

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/244288.html
標籤:python
