Web框架本質
咱們能夠這理解:全部的Web應用本質上就是一個socket服務端,而用戶的瀏覽器就是一個socket客戶端。html
不完善的Web服務端示例
""" 不完善的web服務端示例 """ import socket # 生成socket示例對象 sk = socket.socket() # 綁定IP端口 sk.bind(("127.0.0.1",8080)) # 監聽 sk.listen() # 寫一個死循環,一直等待客戶端來鏈接 while True: # 獲取與客戶端的鏈接 conn,addr = sk.accept() # 接收客戶端發來的消息 data = conn.recv(8096) print(data) # 給客戶端回覆消息 # conn.send(b'http/1.1 200 OK\r\ncontent-type:text/html;charset=utf-8\r\n\r\n') # 想讓瀏覽器在頁面上顯示出來的內容都是響應正文 conn.send(b'<h1>s10</h1>') conn.close() sk.close()
HTTP協議就是對瀏覽器的發送消息和服務端的響應信息做出一個統一的規則python
打印在咱們在服務端接收到的消息
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) # 將瀏覽器發來的消息打印出來 conn.send(b"OK") 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'
HTTP GET請求的格式:
HTTP響應的格式:
通過上面的補充學習,咱們知道了要想讓咱們本身寫的web server端正經起來,必需要讓咱們的Web server在給客戶端回覆消息的時候按照HTTP協議的規則加上響應狀態行,這樣咱們就實現了一個正經的Web框架了。mysql
正經的Web框架
""" 完善的web服務端示例 """ import socket # 生成socket示例對象 sk = socket.socket() # 綁定IP端口 sk.bind(("127.0.0.1",8081)) # 監聽 sk.listen() # 寫一個死循環,一直等待客戶端來鏈接 while True: # 獲取與客戶端的鏈接 conn,addr = sk.accept() # 接收客戶端發來的消息 data = conn.recv(8096) print(data) # 給客戶端回覆消息 conn.send(b'http/1.1 200 OK\r\ncontent-type:text/html;charset=utf-8\r\n\r\n') # 想讓瀏覽器在頁面上顯示出來的內容都是響應正文 conn.send(b'<h1>s10</h1>') # conn.close() # sk.close()
根據不一樣的路徑返回不一樣的內容
""" 完善的web服務端示例 根據不一樣的路徑返回不一樣的內容 """ import socket # 生成socket示例對象 sk = socket.socket() # 綁定IP端口 sk.bind(("127.0.0.1",8082)) # 監聽 sk.listen() # 寫一個死循環,一直等待客戶端來鏈接 while True: # 獲取與客戶端的鏈接 conn,addr = sk.accept() # 接收客戶端發來的消息 data = conn.recv(8096) data_str = str(data,encoding="utf-8") print(data_str) # 用\r\n去切割上面的字符串 l1 = data_str.split("\r\n") print(l1[0]) # 按照空格去切割上面的字符串 l2 = l1[0].split() url = l2[1] print(url) # 給客戶端回覆消息 conn.send(b'http/1.1 200 OK\r\ncontent-type:text/html;charset=utf-8\r\n\r\n') # 想讓瀏覽器在頁面上顯示出來的內容都是響應正文 if url == "/yimi/": response = b'<h1>hello yimi</h1>' elif url == "/xiaohei/": response = b'<h1>hello xiaohei</h1>' else: response = b'<h1>404!not fund!</h1>' conn.send(response) # conn.close() # sk.close()
根據不一樣的路徑返回不一樣的內容——函數版
""" 完善的web服務端示例 函數版根據不一樣的路徑返回不一樣的內容 """ import socket # 生成socket示例對象 sk = socket.socket() # 綁定IP端口 sk.bind(("127.0.0.1",8083)) # 監聽 sk.listen() # 定義一個處理/yimi/的函數 def yimi(url): ret = 'hello {}'.format(url) return bytes(ret,encoding="utf-8") # 定義一個處理/xiaohei/的函數 def xiaohei(url): ret = 'hello {}'.format(url) return bytes(ret,encoding="utf-8") # 寫一個死循環,一直等待客戶端來鏈接 while True: # 獲取與客戶端的鏈接 conn,addr = sk.accept() # 接收客戶端發來的消息 data = conn.recv(8096) data_str = str(data,encoding="utf-8") print(data_str) # 用\r\n去切割上面的字符串 l1 = data_str.split("\r\n") print(l1[0]) # 按照空格去切割上面的字符串 l2 = l1[0].split() url = l2[1] print(url) # 給客戶端回覆消息 conn.send(b'http/1.1 200 OK\r\ncontent-type:text/html;charset=utf-8\r\n\r\n') # 想讓瀏覽器在頁面上顯示出來的內容都是響應正文 # 根據不一樣的url返回不一樣的內容 if url == "/yimi/": response = yimi(url) elif url == "/xiaohei/": response = xiaohei(url) else: response = b'<h1>404!not fund!</h1>' conn.send(response) # conn.close() # sk.close()
根據不一樣的路徑返回不一樣的內容——函數進階版
""" 完善的web服務端示例 函數版根據不一樣的路徑返回不一樣的內容 進階函數版 不寫if判斷了,用url名字去找對應的函數名 """ import socket # 生成socket示例對象 sk = socket.socket() # 綁定IP端口 sk.bind(("127.0.0.1",8084)) # 監聽 sk.listen() # 定義一個處理/yimi/的函數 def yimi(url): ret = 'hello {}'.format(url) return bytes(ret,encoding="utf-8") # 定義一個處理/xiaohei/的函數 def xiaohei(url): ret = 'hello {}'.format(url) return bytes(ret,encoding="utf-8") # 定義一個專門來處理404的函數 def f404(url): ret = "你訪問的這個{}找不到".format(url) return bytes(ret,encoding="utf-8") url_func = [ ("/yimi/",yimi), ("/xiaohei/",xiaohei), ] # 寫一個死循環,一直等待客戶端來鏈接 while True: # 獲取與客戶端的鏈接 conn,addr = sk.accept() # 接收客戶端發來的消息 data = conn.recv(8096) data_str = str(data,encoding="utf-8") print(data_str) # 用\r\n去切割上面的字符串 l1 = data_str.split("\r\n") print(l1[0]) # 按照空格去切割上面的字符串 l2 = l1[0].split() url = l2[1] print(url) # 給客戶端回覆消息 conn.send(b'http/1.1 200 OK\r\ncontent-type:text/html;charset=utf-8\r\n\r\n') # 想讓瀏覽器在頁面上顯示出來的內容都是響應正文 # 根據不一樣的url返回不一樣的內容 # 去url_func裏面找對應關係 for i in url_func: if i[0] == url: func = i[1] break # 找不到對應關係就默認執行404函數 else: func = f404 # 拿到函數的執行結果 response = func(url) # 將函數返回的結果發給瀏覽器 conn.send(response) # conn.close() # sk.close()
返回具體點HTML文件
""" 完善的web服務端示例 函數版根據不一樣的路徑返回不一樣的內容 進階函數版 不寫if判斷了,用url名字去找對應的函數名 返回html頁面 """ import socket # 生成socket示例對象 sk = socket.socket() # 綁定IP端口 sk.bind(("127.0.0.1",8085)) # 監聽 sk.listen() # 定義一個處理/yimi/的函數 def yimi(url): with open("yimi.html","rb") as f: ret = f.read() return ret # 定義一個處理/xiaohei/的函數 def xiaohei(url): with open("xiaohei.html","rb") as f: ret = f.read() return ret # 定義一個專門來處理404的函數 def f404(url): ret = "你訪問的這個{}找不到".format(url) return bytes(ret,encoding="utf-8") url_func = [ ("/yimi/",yimi), ("/xiaohei/",xiaohei), ] # 寫一個死循環,一直等待客戶端來鏈接 while True: # 獲取與客戶端的鏈接 conn,addr = sk.accept() # 接收客戶端發來的消息 data = conn.recv(8096) data_str = str(data,encoding="utf-8") print(data_str) # 用\r\n去切割上面的字符串 l1 = data_str.split("\r\n") print(l1[0]) # 按照空格去切割上面的字符串 l2 = l1[0].split() url = l2[1] print(url) # 給客戶端回覆消息 conn.send(b'http/1.1 200 OK\r\ncontent-type:text/html;charset=utf-8\r\n\r\n') # 想讓瀏覽器在頁面上顯示出來的內容都是響應正文 # 根據不一樣的url返回不一樣的內容 # 去url_func裏面找對應關係 for i in url_func: if i[0] == url: func = i[1] break # 找不到對應關係就默認執行404函數 else: func = f404 # 拿到函數的執行結果 response = func(url) # 將函數返回的結果發給瀏覽器 conn.send(response) # conn.close() # sk.close()
讓網頁動起來
""" 根據URL中不一樣的路徑返回不一樣的內容--函數進階版 返回HTML頁面 讓網頁動態起來 """ import socket import time sk = socket.socket() sk.bind(("127.0.0.1", 8080)) # 綁定IP和端口 sk.listen() # 監聽 # 將返回不一樣的內容部分封裝成函數 def index(url): with open("index.html", "r", encoding="utf8") as f: s = f.read() now = str(time.time()) s = s.replace("@@oo@@", now) # 在網頁中定義好特殊符號,用動態的數據去替換提早定義好的特殊符號 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和實際要執行的函數的對應關係 list1 = [ ("/index/", index), ("/home/", home), ] while 1: # 等待鏈接 conn, add = sk.accept() data = conn.recv(8096) # 接收客戶端發來的消息 # 從data中取到路徑 data = str(data, encoding="utf8") # 把收到的字節類型的數據轉換成字符串 # 按\r\n分割 data1 = data.split("\r\n")[0] url = data1.split()[1] # url是咱們從瀏覽器發過來的消息中分離出的訪問路徑 conn.send(b'HTTP/1.1 200 OK\r\n\r\n') # 由於要遵循HTTP協議,因此回覆的消息也要加狀態行 # 根據不一樣的路徑返回不一樣內容 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()
服務器程序和應用程序
WSGI(Web Server Gateway Interface)就是一種規範,它定義了使用Python編寫的web應用程序與web服務器程序之間的接口格式,實現web應用程序與web服務器程序間的解耦。web
經常使用的WSGI服務器有uwsgi、Gunicorn。而Python標準庫提供的獨立WSGI服務器叫wsgiref,Django開發環境用的就是這個模塊來作服務器。sql
wsgiref
""" 根據URL中不一樣的路徑返回不一樣的內容--函數進階版 返回HTML頁面 讓網頁動態起來 wsgiref模塊版 """ import time from wsgiref.simple_server import make_server # 將返回不一樣的內容部分封裝成函數 def index(url): with open("index.html", "r", encoding="utf8") as f: s = f.read() now = str(time.time()) s = s.replace("@@oo@@", now) 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和實際要執行的函數的對應關係 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()
jinja2
上面的代碼實現了一個簡單的動態,我徹底能夠從數據庫中查詢數據,而後去替換我html中的對應內容,而後再發送給瀏覽器完成渲染。 這個過程就至關於HTML模板渲染數據。 本質上就是HTML內容中利用一些特殊的符號來替換要展現的數據。 我這裏用的特殊符號是我定義的,其實模板渲染有個現成的工具: 數據庫
下載jinja2
pip install jinja2
index2.html文件
<!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>
使用jinja2渲染index2.html文件:
from wsgiref.simple_server import make_server from jinja2 import Template def index(): with open("index2.html", "r") as f: data = f.read() template = Template(data) # 生成模板文件 ret = template.render({"name": "Alex", "hobby_list": ["燙頭", "泡吧"]}) # 把數據填充到模板裏面 return [bytes(ret, encoding="utf8"), ] def home(): with open("home.html", "rb") as f: data = f.read() return [data, ] # 定義一個url和函數的對應關係 URL_LIST = [ ("/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 URL_LIST: if i[0] == url: func = i[1] # 去以前定義好的url列表裏找url應該執行的函數 break if func: # 若是能找到要執行的函數 return func() # 返回函數的執行結果 else: return [bytes("404沒有該頁面", encoding="utf8"), ] if __name__ == '__main__': httpd = make_server('', 8000, run_server) print("Serving HTTP on port 8000...") httpd.serve_forever()
使用python來鏈接數據庫
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()
建立一個測試用的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的語法規則寫上,其內部就會按照指定的語法進行相應的替換,從而達到動態的返回內容。django
Django
安裝
pip3 install django==1.11.9
建立一個django項目
django-admin startproject mysite
運行Django項目
python manage.py runserver 127.0.0.1:8080
模板文件配置
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', ], }, }, ]
靜態文件配置
STATIC_URL = '/static/' # HTML中使用的靜態文件夾前綴 STATICFILES_DIRS = [ os.path.join(BASE_DIR, "static"), # 靜態文件存放位置 ]
HttpResponse
內部傳入一個字符串參數,返回給瀏覽器。瀏覽器
例如:服務器
def index(request): # 業務邏輯代碼 return HttpResponse("OK")
render
除request參數外還接受一個待渲染的模板文件和一個保存具體數據的字典參數。app
將數據填充進模板文件,最後把結果返回給瀏覽器。(相似於咱們上面用到的jinja2)
例如:
def index(request): # 業務邏輯代碼 return render(request, "index.html", {"name": "alex", "hobby": ["燙頭", "泡吧"]})
redirect
接受一個URL參數,表示跳轉到指定的URL。
例如:
def index(request): # 業務邏輯代碼 return redirect("/home/")