咱們能夠這樣理解:全部的Web應用本質上就是一個socket服務端,而用戶的瀏覽器就是一個socket客戶端。 這樣咱們就能夠本身實現Web框架了。css
先寫一個html
import socket sk = socket.socket() sk.bind(('127.0.0.1', 8080)) sk.listen(5) while True: conn, addr = sk.accept() data = conn.recv(1024) print(data) # 打印瀏覽器發過來的消息並分析 conn.send(b'ok') conn.close()
能夠說Web服務本質上都是在這幾行代碼基礎上擴展出來的。這段代碼就是它們的祖宗。前端
用戶的瀏覽器一輸入網址,會給服務端發送數據,那瀏覽器會發送什麼數據?怎麼發?這個誰來定? 你這個網站是這個規定,他那個網站按照他那個規定,這互聯網還能玩麼?python
因此,必須有一個統一的規則,讓你們發送消息、接收消息的時候有個格式依據,不能隨便寫。mysql
這個規則就是HTTP協議,之後瀏覽器發送請求信息也好,服務器回覆響應信息也罷,都要按照這個規則來。jquery
HTTP協議主要規定了客戶端和服務器之間的通訊格式,那HTTP協議是怎麼規定消息格式的呢?web
運行上面的代碼,在瀏覽器輸入服務器的地址和端口sql
獲得瀏覽器發過來的data的打印結果:chrome
# data結果 ''' 1.請求首行: b'GET / HTTP/1.1\r\n 2.請求體:一大堆K:V鍵值對 Host: 127.0.0.1:8080\r\n Connection: keep-alive\r\n Cache-Control: max-age=0\r\n Upgrade-Insecure-Requests: 1\r\n User-Agent: Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36 chrome-extension\r\n Sec-Fetch-Mode: navigate\r\n Sec-Fetch-User: ?1\r\n Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3\r\n Sec-Fetch-Site: cross-site\r\n Accept-Encoding: gzip, deflate, br\r\n Accept-Language: zh-CN,zh;q=0.9,en-GB;q=0.8,en;q=0.7\r\n \r\n 3.請求體: 這裏是請求數據,get請求沒有,post請求才有 '''
咱們發現收發的消息須要按照必定的格式來,數據庫
1.數據格式
get請求格式
請求首行(請求方式,協議版本。。。)
請求頭(一大堆k:v鍵值對)
\r\n
請求體(真正的數據 發post請求的時候纔有 若是是get請求不會有)
響應格式
響應首行
響應頭
\r\n
響應體
HTTP GET請求的格式:
HTTP響應的格式:
這裏就須要瞭解一下HTTP協議了。
HTTP協議對收發消息的格式要求
每一個HTTP請求和響應都遵循相同的格式,一個HTTP包含Header和Body兩部分,其中Body是可選的。 HTTP響應的Header中有一個 Content-Type
代表響應的內容格式。如 text/html
表示HTML網頁。
HTTP協議特色:
超文本傳輸協議
1.四大特性
1.基於TCP/IP之上做用於應用層
2.基於請求響應
3.無狀態 cookie session token...
4.無鏈接
2.響應狀態碼
用特定的數字表示一些意思
1XX:服務端已經成功接收到了你的數據 正在處理 你能夠繼續提交其餘數據
2XX:服務端成功響應(200請求成功)
3XX:重定向
4XX:請求錯誤(404 請求資源不存在 403 拒絕訪問)
5XX:服務器內部錯誤(500 )
若是咱們想要本身寫的web server服務端真正運行起來,達到一種請求與響應的對應關係,咱們必需要在sercer服務端給客戶端回覆消息的時候,按照HTTP協議的規則加上響應狀態行 ,就是 協議版本+狀態碼+狀態描述符:b'HTTP/1.1 200 OK\r\n\r\n'
以下例子:
import socket sk = socket.socket() sk.bind(('127.0.0.1', 8080)) sk.listen(5) while True: conn, addr = sk.accept() data = conn.recv(1024) print(data) # 須要向客服端發送響應頭,客戶端才能正常顯示信息 conn.send(b'HTTP/1.1 200 OK\r\n\r\n') conn.send(b'hello world') conn.close()
若是咱們在瀏覽器客戶端輸入:http://127.0.0.1:8080/home
,瀏覽器的頁面顯示就爲home,那麼能夠這樣作:
import socket sk = socket.socket() sk.bind(('127.0.0.1', 8080)) # 綁定IP和端口 sk.listen(5) # 監聽 while True: # 等待鏈接 conn, addr = sk.accept() # 接收客戶端返回的信息 data = conn.recv(1024) # print(data) # 從data中取到路徑 並將收到的字節類型的數據轉換成字符串 # data = str(data,encoding='utf8') data = data.decode('utf8') # 按\r\n分割 data1 = data.split('\r\n')[0] # print(data1) # 請求首行的進行切割拿到url url是咱們從瀏覽器發來的消息分離出來的訪問路徑 url=data1.split(' ')[1] # print(url) # 必須遵循HTTP協議,須要向客服端發送狀態行,客戶端才能正常顯示信息 conn.send(b'HTTP/1.1 200 OK\r\n\r\n') # 根據不一樣的路徑返回不一樣內容 if url == '/index': response = b'index' elif url == '/home': response = b'home' else: response = b'404 not found!!!' conn.send(response) conn.close()
import socket sk = socket.socket() sk.bind(('127.0.0.1', 8080)) # 綁定IP和端口 sk.listen(5) # 監聽 # 將返回不一樣的內容部分封裝成函數 def index(url): res = f'這是{url}頁面|' return bytes(res,encoding='gbk') # return res.encode('utf-8') def home(url): res = f'這是{url}頁面|' return bytes(res,encoding='gbk') # return res.encode('utf-8') while True: # 等待鏈接 conn, addr = sk.accept() # 接收客戶端返回的信息 data = conn.recv(1024) # print(data) # 從data中取到路徑 並將收到的字節類型的數據轉換成字符串 # data = str(data,encoding='utf8') data = data.decode('utf8') # 按\r\n分割 data1 = data.split('\r\n')[0] # print(data1) # 請求首行的進行切割拿到url url是咱們從瀏覽器發來的消息分離出來的訪問路徑 url=data1.split(' ')[1] # print(url) # 根據不一樣的路徑返回不一樣內容 if url == '/index': response = index(url) elif url == '/home': response = home(url) else: response = b'404 not found!!!' # 必須遵循HTTP協議,須要向客服端發送狀態行,客戶端才能正常顯示信息 conn.send(b'HTTP/1.1 200 OK\r\n\r\n') conn.send(response) conn.close()
import socket sk = socket.socket() sk.bind(('127.0.0.1', 8080)) # 綁定IP和端口 sk.listen(5) # 監聽 # 定義一個url和實際要執行的函數的對應關係 def home(url): res = bytes(url, encoding='utf8') return res def index(url): res = bytes(url, encoding='utf8') return res # 定義一個url和要執行函數對應關係的字典 dt = { '/index':index, '/home':home } while True: # 等待鏈接 conn, addr = sk.accept() # 接收客戶端返回的信息 data = conn.recv(1024) # print(data) # 從data中取到路徑 並將收到的字節類型的數據轉換成字符串 # data = str(data,encoding='utf8') data = data.decode('utf8') # 按\r\n分割 data1 = data.split('\r\n')[0] # print(data1) # 請求首行的進行切割拿到url url是咱們從瀏覽器發來的消息分離出來的訪問路徑 url=data1.split(' ')[1] # print(url) func = None # 根據不一樣的路徑返回不一樣內容 for k,v in dt.items(): print(k,v) if url == k: func = v break if func: response = func(url) else: response = b'404 not found!!!' # 必須遵循HTTP協議,須要向客服端發送狀態行,客戶端才能正常顯示信息 conn.send(b'HTTP/1.1 200 OK\r\n\r\n') conn.send(response) conn.close()
首先建立咱們須要的html頁面,而後把在代碼裏面以rb模式讀取出來,發送到瀏覽器
import socket sk = socket.socket() sk.bind(('127.0.0.1', 8080)) # 綁定IP和端口 sk.listen(5) # 監聽 def home(url): with open('home頁面.html','rb') as fr: res = fr.read() return res def index(url): with open('index頁面.html', 'rb') as fr: res = fr.read() return res # 定義一個url和實際要執行的函數的對應關係 dt = { '/index':'index', '/home':'home' } while True: # 等待鏈接 conn, addr = sk.accept() # 接收客戶端返回的信息 data = conn.recv(1024) # print(data) # 從data中取到路徑 並將收到的字節類型的數據轉換成字符串 # data = str(data,encoding='utf8') data = data.decode('utf8') # 按\r\n分割 data1 = data.split('\r\n')[0] # print(data1) # 請求首行的進行切割拿到url url是咱們從瀏覽器發來的消息分離出來的訪問路徑 url=data1.split(' ')[1] # print(url) func = None # 根據不一樣的路徑返回不一樣內容 for k,v in dt.items(): print(k,v) if url == k: func = v break if func: response = func(url) else: response = b'404 not found!!!' # 必須遵循HTTP協議,須要向客服端發送狀態行,客戶端才能正常顯示信息 conn.send(b'HTTP/1.1 200 OK\r\n\r\n') conn.send(response) conn.close()
上面的網頁是不會變化的,如何實現獲得一個動態的網站呢?下面作個例子:每次刷新都在獲取新的時間,模擬動態的數據
import socket import datetime sk = socket.socket() sk.bind(('127.0.0.1', 8080)) # 綁定IP和端口 sk.listen(5) # 監聽 def home(url): with open('get_time.html', 'r',encoding='utf8') as fr: res = fr.read() now = datetime.datetime.now().strftime("%Y-%m-%d %X") # 在網頁中定義好特殊符號,用動態的數據替換特殊字符 res = res.replace('*time*',now).encode('utf8') return res def index(url): with open('index頁面.html', 'rb') as fr: res = fr.read() return res # 定義一個url和實際要執行的函數的對應關係 dt = { '/index': index, '/home': home } while True: # 等待鏈接 conn, addr = sk.accept() # 接收客戶端返回的信息 data = conn.recv(1024) # print(data) # 從data中取到路徑 並將收到的字節類型的數據轉換成字符串 # data = str(data,encoding='utf8') data = data.decode('utf8') # 按\r\n分割 data1 = data.split('\r\n')[0] # print(data1) # 請求首行的進行切割拿到url url是咱們從瀏覽器發來的消息分離出來的訪問路徑 url=data1.split(' ')[1] # print(url) func = None # 根據不一樣的路徑返回不一樣內容 for k,v in dt.items(): print(k,v) if url == k: func = v break if func: response = func(url) else: response = b'404 not found!!!' # 必須遵循HTTP協議,須要向客服端發送狀態行,客戶端才能正常顯示信息 conn.send(b'HTTP/1.1 200 OK\r\n\r\n') conn.send(response) 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開發環境用的就是這個模塊來作服務器
from wsgiref.simple_server import make_server def run(environ,response): # 當客戶發送請求過來時,會先調用wsgi內部接口,而後調用run函數,而且攜帶了兩個參數給run函數 # environ:一個包含全部HTTP請求信息的dict對象; # response:一個發送HTTP響應的函數。 # 向客戶端發送的狀態碼和頭信息 response('200 OK',[('content-type','text/html; charset=utf-8'),]) # 返回的是一個列表,內容是發送給客戶端展現的內容 return ['hello world'.encode('utf-8')] if __name__ == '__main__': # 至關於socket綁定ip和端口 server = make_server('127.0.0.1',8080,run) # 實時監聽地址,等待客戶端鏈接,有鏈接來了就調用run函數 server.serve_forever()
from wsgiref.simple_server import make_server import datetime def index(url): with open('index頁面.html', 'rb') as fr: data = fr.read() return data def home(url): with open('home頁面.html', 'rb') as fr: data = fr.read() return data def get_time(url): now = datetime.datetime.now().strftime('%Y-%m-%d %X') with open('get_time.html','r',encoding='utf-8') as fr: data = fr.read() data = data.replace('*time*',now) return data.encode('utf-8') dic={ '/index':index, '/home':home, '/get_time':get_time } def run(env,response): # 發送狀態碼和頭信息到客戶端 response('200 ok',[('content-type','text/html;charset=utf-8'),]) # print(env) # 由於env就是客戶端發過來的請求信息(k:v鍵值對形式), # 經過打印信息得出PATH_INFO就是請求的url, url = env.get('PATH_INFO') print(url) func = None if url in dic: func = dic.get(url) if func: res = func(url) else: res = b'404 not found!!!' return [res] if __name__ == '__main__': server = make_server('127.0.0.1',8080,run) server.serve_forever()
jinja2模塊,跟上面的用特殊符號去替換須要展現的內容的原理是同樣的,jinja2他將html頁面封裝成一個能夠渲染數據的模板,而後獲得咱們真正想要返回給瀏覽器的html頁面。
1.建立一張user表:
2.建立html文件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script> <link href="https://cdn.bootcss.com/twitter-bootstrap/3.3.1/css/bootstrap.min.css" rel="stylesheet"> <script src="https://cdn.bootcss.com/twitter-bootstrap/3.3.1/js/bootstrap.min.js"></script> </head> <body> <div class="container"> <div class="row"> <div class="col-md-8 col-md-offset-2"> <h1 class="text-center">用戶列表</h1> <table class="table table-bordered table-striped table-hover"> <thead> <tr> <th>id</th> <th>name</th> <th>pwd</th> </tr> </thead> <tbody> <!--user_list是渲染的數據 --> {% for user_dict in user_list %} <tr> <td>{{ user_dict.id }}</td> <td>{{ user_dict.name }}</td> <td>{{ user_dict.hobby}}</td> </tr> {% endfor %} </tbody> </table> </div> </div> </div> </body> </html>
3.使用jinja2渲染html文件。
from wsgiref.simple_server import make_server from jinja2 import Template import pymysql # 從數據庫中獲取,並使用jinja2將數據渲染到html def get_db(url): conn = pymysql.connect( host='127.0.0.1', port=3306, user='tomjoy', password='123456', database='user_info', charset='utf8', autocommit=True ) cursor = conn.cursor(cursor=pymysql.cursors.DictCursor) sql = "select * from user" cursor.execute(sql) # 1.從數據庫中獲取數據 res = cursor.fetchall() with open('get_db.html','r',encoding='utf8') as fr: data = fr.read() # 2.生成html渲染模板對象 temp = Template(data) # 3.將數據庫中獲取回來的數據,傳到html模板對象進行渲染, # 返回一個咱們真正想要展現的html頁面 ret = temp.render(user_list=res) return ret.encode('utf8') dic = { '/get_db' : get_db } def run(env,response): response('200 ok',[('content-type','text/html;charset=utf-8'),]) func = None url = env.get('PATH_INFO') if url in dic: func = dic.get(url) if func: res = func(url) else: res = b'404 not found!!!' return [res] if __name__ == '__main__': server = make_server('127.0.0.1',8080,run) server.serve_forever()
jinja2模板語法(極其貼近python後端語法)
<p>{{ user }}</p> <p>{{ user.name }}</p> <p>{{ user['pwd'] }}</p> <p>{{ user.get('hobby') }}</p> {% for user_dict in user_list %} <tr> <td>{{ user_dict.id }}</td> <td>{{ user_dict.name }}</td> <td>{{ user_dict.pwd }}</td> </tr> {% endfor %}
模板渲染:利用模板語法 實現後端傳遞數據給前端html頁面
模板語法書寫格式:
變量相關:{{}}
邏輯相關:{%%}
注意:Django的模板語法因爲是本身封裝好的,只支持 點.取值
注:模板渲染的原理就是字符串替換,咱們只要在HTML頁面中遵循jinja2的語法規則寫上,其內部就會按照指定的語法進行相應的替換,從而達到動態的返回內容。
效果以下:
1.安裝django
pip3 install django==1.11.11
2.建立django項目
在cmd命令行下建立一個名爲mysite的Django項目
django-admin startproject mysite
3.目錄介紹
mysite ├── manage.py # Django入口管理文件 └── templates # 存放html文件 └── mysite # 項目目錄 ├── __init__.py ├── settings.py # 配置 ├── urls.py # 路由 --> URL和函數的對應關係 └── wsgi.py # runserver命令就使用wsgiref模塊作簡單的web server
4.模板文件配置
使用命令行建立django項目 不會自動幫你建立templates文件夾, 只能本身建立
在.settings文件中 須要你手動在TEMPLATES的DIRS寫配置
[os.path.join(BASE_DIR, 'templates')]
TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, 'templates')], # templates 文件夾位置 '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', ], }, }, ]
5.啓動django項目
python manage.py runserver
6.建立應用app01
python manage.py startapp app01
7.app應用目錄:
└── app01 # 項目目錄 ├── migrations文件夾 # 存放數據庫遷移記錄 ├── __init__.py ├── admin.py # django後臺管理 └── apps.py # 註冊相關 └── models.py # 模型類 └── tests.py # 測試文件 └── views.py # 存放視圖函數
注意:若是是在命令行下建立app後,須要你去settings配置文件中註冊添加app名字。這樣django項目才能識別到你這個app
8.靜態文件配置:
什麼是靜態文件?
靜態文件就是在打開網頁時所用到的 圖片、 js、css以及第三方的框架bootstrap、fontawesome、sweetalert
一般狀況下 網站所用到的靜態文件資源 統一都放在static文件夾下,爲了方便識別
STATIC_URL = '/static/' # 是訪問靜態資源的接口前綴,並非存放靜態文件的文件夾 """只要你想訪問靜態資源 你就必須以static開頭""" # 手動在settings最底下添加配置靜態文件訪問資源 # 下面都是存放靜態文件的文件夾的路徑 # 從上往下找靜態文件,找不到就報錯 STATICFILES_DIRS = [ os.path.join(BASE_DIR,'static'), os.path.join(BASE_DIR,'static1'), os.path.join(BASE_DIR,'static2'), ]
圖解:
9.禁用中間件:
前期爲了方便表單提交測試。在settings配置文件中暫時禁用csrf中間件
10.重定向:
重定向的意思就是,我訪問的連接不是我剛剛輸入的那個連接,而是我一輸入他就跳轉到了另一個連接,這就是重定向
最後注意事項:
1.計算機的名稱不能有中文
2.一個pycharm窗口就是一個項目
3.項目名裏面儘可能不要用中文
django版本問題
1.X 2.X 如今市面上用的比較多的仍是1.X
推薦使用1.11.9~1.11.13
django安裝
pip3 install django==1.11.11
如何驗證django是否安裝成功
命令行直接敲django-admin
一個django項目就相似因而一所大學,而app就相似於大學裏面的學院
django其實就是用來一個個應用的
一個app就至關於一塊獨立的功能
用戶功能
管理功能
.........
django支持任意多個app