HTTP協議簡介
HTTP是一個客戶端終端(用戶)和服務器端(網站)請求和應答的標准(TCP)。通過使用網
頁瀏覽器、網絡爬蟲或者其它的工具,客戶端發起一個HTTP請求到服務器上指定端口(默認端
口為80. 我們稱這個客戶端為用戶代理程序(user agent)。應答的服務器上存儲着一些資源,
比如HTML文件和圖像。我們稱這個應答服務器為源服務器(origin server)。在用戶代理和源
服務器中間可能存在多個“中間層”,比如代理服務器、網關或者隧道(tunnel)。
盡管TCP/IP協議是互聯網上最流行的應用,HTTP協議中,並沒有規定必須使用它或它支持的
層。事實上,HTTP可以在任何互聯網協議上,或其他網絡上實現。HTTP假定其下層協議提供
可靠的傳輸。因此,任何能夠提供這種保證的協議都可以被其使用。因此也就是其在TCP/IP協
議族使用TCP作為其傳輸層。
通常,由HTTP客戶端發起一個請求,創建一個到服務器指定端口(默認是80端口)的TCP連
接。HTTP服務器則在那個端口監聽客戶端的請求。一旦收到請求,服務器會向客戶端返回一個
狀態,比如"HTTP/1.1 200 OK",以及返回的內容,如請求的文件、錯誤消息、或者其它信息。
HTTP工作原理
HTTP 默認端口是80 ; HTTPs默認端口是443
HTTP協議定義Web客戶端如何從Web服務器請求Web頁面,以及服務器如何把Web頁面傳送給客戶端。HTTP協議采用了請求/響應模型。客戶端向服務器發送一個請求報文,請求報文包含請求的方法、URL、協議版本、請求頭部和請求數據。服務器以一個狀態行作為響應,響應的內容包括協議的版本、成功或者錯誤代碼、服務器信息、響應頭部和響應數據。
以下是 HTTP 請求/響應的步驟:
1. 客戶端連接到Web服務器
一個HTTP客戶端,通常是瀏覽器,與Web服務器的HTTP端口(默認為80)建立一個TCP套接字連接。例如,http://www.baidu.com。
2. 發送HTTP請求
通過TCP套接字,客戶端向Web服務器發送一個文本的請求報文,一個請求報文由請求行、請求頭部、空行和請求數據4部分組成。
3. 服務器接受請求並返回HTTP響應
Web服務器解析請求,定位請求資源。服務器將資源復本寫到TCP套接字,由客戶端讀取。一個響應由狀態行、響應頭部、空行和響應數據4部分組成。
4. 釋放連接TCP連接
若connection 模式為close,則服務器主動關閉TCP連接,客戶端被動關閉連接,釋放TCP連接;若connection 模式為keepalive,則該連接會保持一段時間,在該時間內可以繼續接收請求;
5. 客戶端瀏覽器解析HTML內容
客戶端瀏覽器首先解析狀態行,查看表明請求是否成功的狀態代碼。然后解析每一個響應頭,響應頭告知以下為若干字節的HTML文檔和文檔的字符集。客戶端瀏覽器讀取響應數據HTML,根據HTML的語法對其進行格式化,並在瀏覽器窗口中顯示。
例如:在瀏覽器地址欄鍵入URL,按下回車之后會經歷以下流程:
- 瀏覽器向 DNS 服務器請求解析該 URL 中的域名所對應的 IP 地址;
- 解析出 IP 地址后,根據該 IP 地址和默認端口 80,和服務器建立TCP連接;
- 瀏覽器發出讀取文件(URL 中域名后面部分對應的文件)的HTTP 請求,該請求報文作為 TCP 三次握手的第三個報文的數據發送給服務器;
- 服務器對瀏覽器請求作出響應,並把對應的 html 文本發送給瀏覽器;
- 釋放 TCP連接;
- 瀏覽器將該 html 文本並顯示內容;
HTTP請求方法
HTTP/1.1 協議中一共定義了八種方法(也叫'動作') 來以不同方式操作指定的資
源:
get : 向指定的資源發出'顯示'請求.使用get方法應該只用在讀取資源數據,而不
應該被用於產生'副作用'的操作中,例如在Web Application中.其中一個原因是
get可能會被網絡蜘蛛等隨意訪問.
head :與get方法一樣,都是向服務器發出指定資源的請求.只不過服務器將不傳
回資源的文本部分.它的好處在於,使用這個方法可以在不必傳輸全部內容的情
況下,就可以獲取其中'關於該資源的信息'(元信息或元數據).
post : 向指定資源提交數據,請求服務器進行處理(例如提交表單或者上傳文件).
數據被包含在請求本文中,這個請求可能會創建新的資源或修改現有資源,或二
者皆有.
put : 向指定資源位置上傳其最新內容.
delete : 請求服務器刪除 request-url所以標識的資源.
trace : 回顯服務器收到的請求,主要用於測試或診斷.
options : 這個方法可以使服務器傳回該資源所支持的所有HTTP請求方法.用 *
來代替資源名稱,向Web服務器發送options請求,可以測試服務器功能是否正常
運作.
connect : HTTP/1.1 協議中預留給能夠將連接改為管道方式的代理服務器.通常
用於ssl加密服務器的連接(僅有非加密的HTTP代理服務武器).
注意:
- 方法名稱是區分大小寫的。當某個請求所針對的資源不支持對應的請求方法的時候,服務器應當返回狀態碼405(Method Not Allowed),當服務器不認識或者不支持對應的請求方法的時候,應當返回狀態碼501(Not Implemented)。
- HTTP服務器至少應該實現GET和HEAD方法,其他方法都是可選的。當然,所有的方法支持的實現都應當匹配下述的方法各自的語義定義。此外,除了上述方法,特定的HTTP服務器還能夠擴展自定義的方法。例如PATCH(由 RFC 5789 指定的方法)用於將局部修改應用到資源.
HTTP狀態碼
所有HTTP響應的第一行都是狀態行,依次是當前HTTP版本號,3位數組成的狀
態代碼,以及描述的短語,彼此由空格分隔.
狀態碼的第一個數字代表當前響應的類型:
-
- 1xx消息——請求已被服務器接收,繼續處理
- 2xx成功——請求已成功被服務器接收、理解、並接受
- 3xx重定向——需要后續操作才能完成這一請求
- 4xx請求錯誤——請求含有詞法錯誤或者無法被執行
- 5xx服務器錯誤——服務器在處理某個正確請求時發生錯誤
雖然RFC2616中已經推薦了描述狀態的短語,例如'200 OK','404 Not Found',
但是WEB開發者仍然能夠自行決定采用何種短語,用以顯示本地化的狀態描述
或者自定義信息.
URL
超文本傳輸協議(HTTP)的統一資源定位符將從因特網獲取的五個基本元素包
括在一個簡單的地址中:
- 傳送協議。
- 層級URL標記符號(為[//],固定不變)
- 訪問資源需要的憑證信息(可省略)
- 服務器。(通常為域名,有時為IP地址)
- 端口號。(以數字方式表示,若為HTTP的默認值“:80”可省略)
- 路徑。(以“/”字符區別路徑中的每一個目錄名稱)
- 查詢。(GET模式的窗體參數,以“?”字符為起點,每個參數以“&”隔開,再以“=”分開參數名稱與數據,通常以UTF8的URL編碼,避開字符沖突的問題)
- 片段。以“#”字符為起點
例:http://baidu.com:80/news/index.html?id=250&page=1 其中:
http,是協議; www.baidu.com 是服務器;
80 是服務器上的網絡端口; /news/index.html 是路徑;
?id=250&page=1 是查詢;
大多數網頁瀏覽器不要求用戶輸入網頁中“http://”的部分,因為絕大多數網頁內容是超文本傳輸協議文件。同樣,“80”是超文本傳輸協議文件的常用端口號,因此一般也不必寫明。一般來說用戶只要輸入統一資源定位符的一部分(www.luffycity.com:80/news/index.html?id=250&page=1)就可以了。
由於超文本傳輸協議允許服務器將瀏覽器重定向到另一個網頁地址,因此許多服務器允許用戶省略網頁地址中的部分,比如 www。從技術上來說這樣省略后的網頁地址實際上是一個不同的網頁地址,瀏覽器本身無法決定這個新地址是否通,服務器必須完成重定向的任務。
請求和響應
HTTP協議中:
瀏覽器給服務端發送消息的過程叫請求
服務端給瀏覽器回復消息的過程叫響應.
HTTP請求格式
HTTP響應格式
Web開發的本質:
互聯網上倆台機器之間通信: ip 端口 協議
瀏覽器輸入URL 回車返回頁面的過程:
域名->DNS解析->ip地址->服務端->返回消息->瀏覽器
服務器把寫好的HTML頁面,返回給瀏覽器,瀏覽器按照HTML格式渲染(顯示)
web框架
web本質就是一個socket服務端,而用戶瀏覽器就是一個socket客戶端.但是不
同的瀏覽器(客戶端)就有不同的規則,所以在與服務端互動時就需要統一格式,
HTTP協議主要規定可客戶端與服務器之間的通信格式.
首先看服務端接收到的消息:
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
Host: 127.0.0.1:8080
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
DNT: 1
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: csrftoken=RKBXh1d3M97iz03Rpbojx1bR6mhHudhyX5PszUxxG3bOEwh1lxFpGOgWN93ZH3zv\r\n\r\n'
再看訪問博客園時瀏覽器收到的響應消息:
點擊view source之后顯示如下圖:
HTTP協議對收發消息的格式要求:
每個HTTP請求和響應都遵循相同格式,一個HTTP包含header和body倆部
分,其中body是可選的.HTTP響應的header中有一個content-type表明響應
的內容格式.如:text/html 表示HTML網頁.
HTTP GET請求格式:
HTTP響應格式:
自定義web框架
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) # 給回復的消息加上響應狀態行 conn.send(b"HTTP/1.1 200 OK\r\n\r\n") conn.send(b"OK") conn.close()
根據不同的路徑返回不同的內容:
"""
根據URL中不同的路徑返回不同的內容
"""
import socket
sk = socket.socket() sk.bind(("127.0.0.1", 8080)) # 綁定IP和端口 sk.listen() # 監聽 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協議,所以回復的消息也要加狀態行 # 根據不同的路徑返回不同內容 if url == "/index/": response = b"index" elif url == "/home/": response = b"home" else: response = b"404 not found!" conn.send(response) conn.close()
根據不同的路徑返回不同內容(函數版)
"""
根據URL中不同的路徑返回不同的內容--函數版
"""
import socket
sk = socket.socket() sk.bind(("127.0.0.1", 8080)) # 綁定IP和端口 sk.listen() # 監聽 # 將返回不同的內容部分封裝成函數 def index(url): s = "這是{}頁面!".format(url) return bytes(s, encoding="utf8") def home(url): s = "這是{}頁面!".format(url) return bytes(s, encoding="utf8") 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協議,所以回復的消息也要加狀態行 # 根據不同的路徑返回不同內容,response是具體的響應體 if url == "/index/": response = index(url) elif url == "/home/": response = home(url) else: response = b"404 not found!" conn.send(response) conn.close()
根據不同的路徑返回不同的內容(函數進階)
"""
根據URL中不同的路徑返回不同的內容--函數進階版
"""
import socket
sk = socket.socket() sk.bind(("127.0.0.1", 8080)) # 綁定IP和端口 sk.listen() # 監聽 # 將返回不同的內容部分封裝成函數 def index(url): s = "這是{}頁面!".format(url) return bytes(s, encoding="utf8") def home(url): s = "這是{}頁面!".format(url) 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()
讓網頁動態起來
選擇使用字符串替換來實現這個需求(這里使用時間戳來模擬動態數據)
"""
根據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()
python web框架分類
a. 收發socket消息 , 按照HTTP協議解析消息 web服務
wsgiref,gunicorn,uWSGI
b. 字符串替換
c.業務邏輯處理
web 應用程序:
自己實現 a b c 的 tornado
自己實現b c 使用別人a Django
自己實現c 使用別人的a b flask
安裝:
在py的安裝文件中找出scripts文件夾,shift+右鍵 打開powershell:
pip install django==1.11.11
創建Django項目:
在cmd命令行中創建: django-admin startproject 項目名
在pycharm中創建: 專業版(自己建) 非專業版(沒有)
在命令行中運行 Django項目:
Python manage.py runserver 127.0.0.1:8000(默認本機端口,可自己
設置)
停止 : Ctrl + c
目錄介紹:
mysite/
├── manage.py # 管理文件
└── mysite # 項目目錄
├── __init__.py ├── settings.py # 配置 ├── urls.py # 路由 --> URL和函數的對應關系 └── wsgi.py # runserver命令就使用wsgiref模塊做簡單的web server
db.sqlite3 Django 開發環境默認的文件數據庫
模板文件配置(配置調用的文件路徑):
#跟html相關的文件配置
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, "template")],# 路徑拼接 BASE_DIR(指項目的根目錄)和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',
],
},
},
]
靜態文件配置(js css images等文件路徑):
在調用時能夠依次查找需要調用的文件了路徑
STATIC_URL = '/static/' # HTML中使用的靜態文件夾前綴
STATICFILES_DIRS = [
os.path.join(BASE_DIR, "static"), # 靜態文件存放位置,進行靜態文件編輯可以在static文件中進行。
]
Django三種基礎模塊
from django.shortcuts import HttpResponse, render, redirect
HttpResponse 內部傳入一個字符串參數,返回給瀏覽器.
例如:
def index(request):
#業務邏輯
return HttpResponse('ok')
render
除request參數外還接受一個待渲染的模板文件和一個保存具體數據的字典參數。
將數據填充進模板文件,最后把結果返回給瀏覽器。
def index(request):
# 業務邏輯代碼
return render(request, "index.html", {"name": "alex", "hobby": ["燙頭", "泡吧"]})
redirect
接受一個URL參數 , 表示跳轉到指定的URL.
def index(request):
# 業務邏輯代碼
return redirect("/home/")
重新定向
普通請求響應: 瀏覽器發一個消息,服務器回一個
重定向請求響應: 瀏覽器發一個消息,服務器發一個跳轉的消息,瀏覽器
根據這條消息向服務器發送請求,服務器再返回一條信息.
瀏覽器(客戶端)與服務端的交互過程:
---瀏覽器發送請求(地址欄輸入URL)
--- 根據URL匹配要訪問的路徑即查找要執行的函數
--- Django找到調用的函數,把與請求相關的數據由request參數進行傳輸.
--- 去指定文件夾找到相關文件,打開讀取內容,按照HTTP響應的格式給瀏
覽器返回讀取內容
--- 瀏覽器收到返回的響應消息並按照HTTP格式顯示消息.
form表單提交數據的三個要素:
1. form表單標簽必須要有action和method屬性;
action鍵的值表示html頁面中信息的提交地址, method鍵的值表示用什
么方法提交
2. 所有獲取用戶輸入的標簽必須放在form表單中,必須要有name屬性;
name鍵的值表示要提交的信息
3. 必須要有submit按鈕.
request相關屬性:
request是指瀏覽器給服務端發送的請求.
1. request.method --返回的是請求的方法(全大寫):GET POST...
注意:觀看method源碼得知:
在HTML頁面中經常會有 method='post' 之類的對method規定獲取值時為小寫,此時
大小寫都可以,當從method中取值時,'upper'會把method中的請求方法全部大寫.
2. request.GET --取得URL里的參數,類似於字典的數據結構(如果用get方
法提交,就要用GET獲取)
3. request.POST -- form表單提交的數據,類似於字典的數據結構(若用post方法
提交就要用POST獲取)
Django的模板語言
{{變量名}}
連接mysql
使用ORM(Object Relationship Model)來翻譯SQL語句.
優點: 開發效率高 ; 開發不用直接寫SQL語句
缺點: 執行效率低
Django項目中創建app應用
在項目中創建一個Python包,不同的功能放在不同的包里,
創建app -- Python manage.py startapp app名稱(例:app01)
在Django中表明創建了一個app :
在settings.py中找到INSTALLED_APPS,添加新建的app
Django中ORM的使用
用處: 操作數據表 操作數據行
使用:
1. 手動創建一個數據庫(ORM不能創建數據庫) --- create database
mysite;
2. 在Django中表明要連接的數據庫
3. 表明連接數據庫的途徑 (利用第三方的包)
在Django中表明用pymysql模塊代替默認的MySQLdb去連接MySQL
數據庫:
在settings.py同級的_init_.py文件中配置:
4. 在app應用下的models.py文件中創建類(類名自定),該類必須繼承
models.Model,可以用ORM語言在創建的類中編寫表結構
5. 在Terminal中執行命令與句,完成對數據庫中表的操作
python manage.py makemigrations -- 記錄models.py中的變更,更新要
對數據庫進行的操作
python manage.py migrate -- 把變更記錄翻譯成SQL語句,在數據庫中
完成操作.
連接數據庫
點擊database 創建數據庫,連接數據庫:
ORM查詢
User.objects.filter(email='', pwd='')