第一章 前后端分離
1.1 什么是前后端分離
前端: 即客戶端,負責渲染用戶顯示界面【如web的js動態渲染頁面, 安卓, IOS,pc客戶端等】
后端:即服務器端,負責接收http請求,處理數據
API:Application Programming Interface 是一些預先定義的函數,或指軟件系統不同組成部分銜接的約定
前后端分離 完整請求過程
1,前端通過http請求后端API
2,后端以json形式返回前端數據
3,前端生成用戶顯示界面【如html , ios , android】
判斷前后端分離得核心標准: 誰生成顯示頁面
1,后端生成【前后端未分離】 django -> HttpResponse(html)/render()
2, 前端生成【前后端分離】
后端給前端的只是數據,例如:jsonResponse()
1.2 優點
1,各司其職
前端:視覺層面,兼容性,前端性能優化
后端:並發,可用性,性能
2,前端可以有效利用客戶端處理數據,有效降低服務端壓力
3,服務端錯誤不會直觀的反饋到用戶
4,后端靈活搭配各類前端 - 如安卓等
5,前端+后端可完全並行開發,加快開發效率
1.3 分離常見問題
問題 | 答案 |
---|---|
如何解決http無狀態? | 采用token(詳情見下方章節) |
如果前端為JS,如何解決跨域問題? | 采用CORS(詳情見下方章節) |
如何解決csrf問題 | 采用token |
是否會影響Search Engine Optimization效果 | 會,前后端分離后,往往頁面不存在靜態文字【例如新聞的詳細內容】 |
”老板,這個邏輯到底是讓前端做還是后端做啊?“ | 底線原則: 數據校驗需要前后端都做 |
”老板,前端工作壓力太大了啊“ | 團隊協作不能只是嘴上說說 |
動靜分離和前后端分離是一個意思么? | 動靜分離指的是 css/js/img這類靜態資源跟服務器拆開部署,典型方案-靜態資源交由CDN廠商處理 |
1.4 實現方式
1,Django 后端只返回json
2, 前端 -> ex: js向服務器發出ajax請求,獲取數據,拿到數據后動態生成html
3, 前端服務和后端服務 分開部署
第二章 token - 令牌
學前須知:
1,base64 '防君子不防小人'
方法 | 作用 | 參數 | 返回值 |
---|---|---|---|
b64encode | 將輸入的參數轉化為base64規則的串 | 預加密的明文,類型為bytes;例:b‘guoxiaonao’ | base64對應編碼的密文,類型為bytes;例:b'Z3VveGlhb25hbw==' |
b64decode | 將base64串 解密回 明文 | base64密文,類型為bytes;例:b'Z3VveGlhb25hbw==' | 參數對應的明文,類型為bytes;例:b'guoxiaonao' |
urlsafe_b64encode | 作用同b64encode,但是會將 '+'替換成 '-',將'/'替換成'_' | 同b64encode | 同b64encode |
urlsafe_b64decode | 作用同b64decode | 同b64decode | 同b64decode |
代碼演示:
import base64 #base64加密 s = b'guoxiaonao' b_s = base64.b64encode(s) #b_s打印結果為 b'Z3VveGlhb25hbw==' #base64解密 ss = base64.b64decode(b_s) #ss打印結果為 b'guoxiaonao'
2,SHA-256 安全散列算法的一種(hash)
md5(128) sha1(128) sha2(256\384)
hash三大特點:
1)定長輸出 2)不可逆 3) 雪崩(輸入敏感性)
雪崩:原始數據的微小修改都會造成結果(hash值)的完全不同。
import hashlib s = hashlib.sha256() #創建sha256對象 s.update(b'xxxx') #添加欲hash的內容,類型為 bytes s.digest() #獲取最終結果 s.digest() #在計算中使用 s.hexdigest() #在傳輸或存儲中使用(與s.digest()應用場景不同)
3,HMAC-SHA256 是一種通過特別計算方式之后產生的消息認證碼,使用散列算法同時結合一個加密密鑰。它可以用來保證數據的完整性,同時可以用來作某個消息的身份驗證
import hmac #生成hmac對象 #第一個參數為加密的key,bytes類型, #第二個參數為欲加密的串,bytes類型 #第三個參數為hmac的算法,指定為SHA256 h = hmac.new(key, str, digestmod='SHA256') h.digest() #獲取最終結果
2.1 JWT - json-web-token
1,三大組成
1,header
格式為字典-元數據格式如下
{'alg':'HS256', 'typ':'JWT'} #alg代表要使用的 算法 #typ表明該token的類別 - 此處必須為 大寫的 JWT
該部分數據需要轉成json串並用base64 加密
2,payload
格式為字典-此部分分為公有聲明和私有聲明
公共聲明:JWT提供了內置關鍵字用於描述常見的問題
此部分均為可選項,用戶根據自己需求 按需添加key,常見公共聲明如下:
{'exp':xxx, # Expiration Time 此token的過期時間的時間戳 'iss':xxx,# (Issuer) Claim 指明此token的簽發者 'aud':xxx, #(Audience) Claim 指明此token的 'iat':xxx, # (Issued At) Claim 指明此創建時間的時間戳 'aud':xxx, # (Audience) Claim 指明此token簽發面向群體 }
私有聲明:用戶可根據自己業務需求,添加自定義的key,例如如下:
{'username': 'guoxiaonao'}
公共聲明和私有聲明均在同一個字典中;轉成json串並用base64加密
3,signature 簽名
簽名規則如下:
根據header中的alg確定 具體算法,以下用 HS256為例
HS256(自定義的key , base64后的header + '.' + base64后的payload)
解釋:用自定義的key, 對base64后的header + '.' + base64后的payload進行hmac計算
2,jwt結果格式
base64(header) + '.' + base64(payload) + '.' + base64(sign)
最終結果如下: b'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6Imd1b3hpYW9uYW8iLCJpc3MiOiJnZ2cifQ.Zzg1u55DCBqPRGf9z3-NAn4kbA-MJN83SxyLFfc5mmM'
3,校驗jwt規則
1,解析header, 確認alg
2,簽名校驗 - 根據傳過來的header和payload按 alg指明的算法進行簽名,將簽名結果和傳過來的sign進行對比,若對比一致,則校驗通過
3,獲取payload自定義內容
4,pyjwt
1,安裝 pip3 install pyjwt
方法 | 參數說明 | 返回值 |
---|---|---|
encode(payload, key, algorithm) | payload: jwt三大組成中的payload,需要組成字典,按需添加公有聲明和私有聲明 例如: {'username': 'guoxiaonao', 'exp': 1562475112} 參數類型: dict |
token串 返回類型:bytes |
key : 自定義的加密key 參數類型:str |
||
algorithm: 需要使用的加密算法HS256 參數類型:str |
||
decode(token,key,algorithm,) | token: token串 參數類型: bytes/str |
payload明文 返回類型:dict |
key : 自定義的加密key ,需要跟encode中的key保持一致 參數類型:str |
||
algorithm: 同encode | ||
issuer: 發布者,若encode payload中添加 'iss' 字段,則可針對該字段校驗 參數類型:str |
若iss校驗失敗,則拋出jwt.InvalidIssuerError | |
audience:簽發的受眾群體,若encode payload中添加'aud'字段,則可針對該字段校驗 參數類型:str |
若aud校驗失敗,則拋出jwt.InvalidAudienceError |
PS: 若encode得時候 payload中添加了exp字段; 則exp字段得值需為 當前時間戳+此token得有效期時間, 例如希望token 300秒后過期 {'exp': time.time() + 300}; 在執行decode時,若檢查到exp字段,且token過期,則拋出jwt.ExpiredSignatureError
第三章 CORS - Cross-origin resource sharing - 跨域資源共享
3.1 什么是CORS
允許瀏覽器向跨源(協議 + 域名 + 端口)服務器,發出XMLHttpRequest請求,從而克服了AJAX只能同源使用的限制
3.2 特點
1,瀏覽器自動完成(在請求頭中加入特殊頭 或 發送特殊請求)
2,服務器需要支持(響應頭中需要有特殊頭)
3.3 簡單請求(Simple requests)和預檢請求(Preflighted requests)
滿足以下全部條件的請求為 簡單請求
1,請求方法如下:
GET or HEAD or POST
2,請求頭僅包含如下:
Accept
Accept-Language
Content-Language
Content-Type
3,Content-Type 僅支持如下三種:
application/x-www-form-urlencoded
multipart/form-data
text/plain
不滿足以上任意一點的請求都是 預檢請求
3.4 簡單請求發送流程
1,請求
請求頭中 攜帶 Origin,該字段表明自己來自哪個域
2,響應
如果請求頭中的Origin在服務器接受范圍內, 則返回如下頭
響應頭 | 作用 | 備注 |
---|---|---|
Access-Control-Allow-Origin | 服務器接受得域 | |
Access-Control-Allow-Credentials | 是否接受Cooike | 可選 |
Access-Control-Expose-Headers | 默認情況下,xhr只能拿到如下響應頭:Cache-Control,Content-Language,Content-Type,Expires,Last-Modified;如果有需要獲取其他頭,需在此指定 | 可選 |
如果服務器不接受此域,則響應頭中不包含 Access-Control-Allow-Origin
3.5 預檢請求發送流程
1,OPTION 請求發起,攜帶如下請求頭
請求頭 | 作用 | 備注 |
---|---|---|
Origin | 表明此請求來自哪個域 | 必選 |
Access-Control-Request-Method | 此次請求使用方法 | 必選 |
Access-Control-Request-Headers | 此次請求使用的頭 | 必選 |
2,OPTION 接受響應階段,攜帶如下響應頭
響應頭 | 作用 | 備注 |
---|---|---|
Access-Control-Allow-Origin | 同簡單請求 | 必選 |
Access-Control-Allow-Methods | 告訴瀏覽器,服務器接受得跨域請求方法 | 必選 |
Access-Control-Allow-Headers | 返回所有支持的頭部,當request有 ‘Access-Control-Request-Headers’時,該響應頭必然回復 |
必選 |
Access-Control-Allow-Credentials | 同簡單請求 | 可選 |
Access-Control-Max-Age | OPTION請求緩存時間,單位s | 可選 |
3,主請求階段
請求頭 | 作用 | 備注 |
---|---|---|
Origin | 表明此請求來自哪個域 |
4,主請求響應階段
響應頭 | 作用 | 備注 |
---|---|---|
Access-Control-Allow-Origin | 當前服務器接受得域 |
3.6 Django支持
django-cors-headers官網 https://pypi.org/project/django-cors-headers/
直接pip 將把django升級到2.0以上,強烈建議用離線安裝方式
配置流程
1,INSTALLED_APPS 中添加 corsheaders 2,MIDDLEWARE 中添加 corsheaders.middleware.CorsMiddleware 位置盡量靠前,官方建議 ‘django.middleware.common.CommonMiddleware’ 上方 3,CORS_ORIGIN_ALLOW_ALL 布爾值 如果為True 白名單不啟用 4,CORS_ORIGIN_WHITELIST =[ "https://example.com" ] 5, CORS_ALLOW_METHODS = ( 'DELETE', 'GET', 'OPTIONS', 'PATCH', 'POST', 'PUT', ) 6, CORS_ALLOW_HEADERS = ( 'accept-encoding', 'authorization', 'content-type', 'dnt', 'origin', 'user-agent', 'x-csrftoken', 'x-requested-with', ) 7, CORS_PREFLIGHT_MAX_AGE 默認 86400s 8, CORS_EXPOSE_HEADERS [] 9, CORS_ALLOW_CREDENTIALS 布爾值, 默認False
第四章 RESTful -Representational State Transfer
4.1,什么是RESTful
1,資源 (Resources)
網絡上的一個實體,或者說是網絡上的一個具體信息,並且每個資源都有一個獨一無二得URI與之對應;獲取資源-直接訪問URI即可
2,表現層(Representation)
如何去表現資源 - 即資源得表現形式;如HTML , xml , JPG , json等
3,狀態轉化(State Transfer)
訪問一個URI即發生了一次 客戶端和服務端得交互;此次交互將會涉及到數據和狀態得變化
客戶端需要通過某些方式觸發具體得變化 - HTTP method 如 GET, POST,PUT,PATCH,DELETE 等
4.2 RESTful的特征
1,每一個URI代表一種資源
2,客戶端和服務器端之前傳遞着資源的某種表現
3,客戶端通過HTTP的幾個動作 對 資源進行操作 - 發生‘狀態轉化’
4.3 如何設計符合RESTful 特征的API
1,協議 - http/https
2,域名:
域名中體現出api字樣,如
or
3, 版本:
4,路徑 -
路徑中避免使用動詞,資源用名詞表示,案例如下
https://api.example.com/v1/users
https://api.example.com/v1/animals
5,HTTP動詞語義
-
GET(SELECT):從服務器取出資源(一項或多項)。
-
POST(CREATE):在服務器新建一個資源。
-
PUT(UPDATE):在服務器更新資源(客戶端提供改變后的完整資源)。
-
PATCH(UPDATE):在服務器更新資源(客戶端提供改變的屬性)。
-
DELETE(DELETE):從服務器刪除資源。
具體案例如下:
GET /zoos:列出所有動物園 POST /zoos:新建一個動物園 GET /zoos/ID:獲取某個指定動物園的信息 PUT /zoos/ID:更新某個指定動物園的信息(提供該動物園的全部信息) PATCH /zoos/ID:更新某個指定動物園的信息(提供該動物園的部分信息) DELETE /zoos/ID:刪除某個動物園 GET /zoos/ID/animals:列出某個指定動物園的所有動物 DELETE /zoos/ID/animals/ID:刪除某個指定動物園的指定動物
6,巧用查詢字符串
?limit=10:指定返回記錄的數量 ?offset=10:指定返回記錄的開始位置。 ?page=2&per_page=100:指定第幾頁,以及每頁的記錄數。 ?sortby=name&order=asc:指定返回結果按照哪個屬性排序,以及排序順序。 ?type_id=1:指定篩選條件
7,狀態碼
1,用HTTP響應碼表達 此次請求結果,例如
200 OK - [GET]:服務器成功返回用戶請求的數據 201 CREATED - [POST/PUT/PATCH]:用戶新建或修改數據成功。 202 Accepted - [*]:表示一個請求已經進入后台排隊(異步任務) 204 NO CONTENT - [DELETE]:用戶刪除數據成功。 400 INVALID REQUEST - [POST/PUT/PATCH]:用戶發出的請求有錯誤,服務器沒有進行新建或修改數據的操作,該操作是冪等的。 401 Unauthorized - [*]:表示用戶沒有權限(令牌、用戶名、密碼錯誤)。 403 Forbidden - [*] 表示用戶得到授權(與401錯誤相對),但是訪問是被禁止的。 404 NOT FOUND - [*]:用戶發出的請求針對的是不存在的記錄,服務器沒有進行操作,該操作是冪等的。 406 Not Acceptable - [GET]:用戶請求的格式不可得(比如用戶請求JSON格式,但是只有XML格式)。 410 Gone -[GET]:用戶請求的資源被永久刪除,且不會再得到的。 422 Unprocesable entity - [POST/PUT/PATCH] 當創建一個對象時,發生一個驗證錯誤。 500 INTERNAL SERVER ERROR - [*]:服務器發生錯誤
2, 自定義內部code 進行響應
如 返回結構如下 {'code':200, 'data': {}, 'error': xxx}
8,返回結果
根據HTTP 動作的不同,返回結果的結構也有所不同
GET /users:返回資源對象的列表(數組) GET /users/guoxiaonao:返回單個資源對象 POST /users:返回新生成的資源對象 PUT /users/guoxiaonao:返回完整的資源對象 PATCH /users/guoxiaonao:返回完整的資源對象 DELETE /users/guoxiaonao:返回一個空文檔