網站首頁 編程語言 正文
引入DjangoRESTframework
- web應用模式
- 1 前后端不分離
- 2 前后端分離
- 認識RESTful
- RESTful設計方法
- 1. 域名
- 2. 版本(Versioning)
- 3. 路徑(Endpoint)
- 3. HTTP動詞
- 4. 過濾信息(Filtering)
- 6. 狀態碼(Status Codes)
- 7. 錯誤處理(Error handling)
- 8. 返回結果
- 9. 其他
- 使用Django開發REST接口
- 測試
- 明確REST接口開發的核心任務
- 序列化Serialization
- 總結
- DjangoRESTframework簡介
- 認識Django REST framework
- 特點
- 筆記
在本章中,我們要大家介紹為什么學習Django REST framework,它能幫助我們做哪些事情。
課程思路:
我們從分析現在流行的前后端分離Web應用模式說起,然后介紹如何設計REST API,通過使用Django來實現一個REST API為例,明確后端開發REST API要做的最核心工作,然后介紹Django REST framework能幫助我們簡化開發REST API的工作。
web應用模式
在開發Web應用中,有兩種應用模式:
- 前后端不分離
- 前后端分離
1 前后端不分離
在前后端不分離的應用模式中,前端頁面看到的效果都是由后端控制,由后端渲染頁面或重定向,也就是后端需要控制前端的展示,前端與后端的耦合度很高。
這種應用模式比較適合純網頁應用,但是當后端對接App時,App可能并不需要后端返回一個HTML網頁,而僅僅是數據本身,所以后端原本返回網頁的接口不再適用于前端App應用,為了對接App后端還需再開發一套接口。
2 前后端分離
前后端不分離,兩個服務器
在前后端分離的應用模式中,后端僅返回前端所需的數據,不再渲染HTML頁面,不再控制前端的效果。至于前端用戶看到什么效果,從后端請求的數據如何加載到前端中,都由前端自己決定,網頁有網頁的處理方式,App有App的處理方式,但無論哪種前端,所需的數據基本相同,后端僅需開發一套邏輯對外提供數據即可。
在前后端分離的應用模式中 ,前端與后端的耦合度相對較低。
在前后端分離的應用模式中,我們通常將后端開發的每個視圖都稱為一個接口,或者API,前端通過訪問接口來對數據進行增刪改查。
前后端分類,js渲染,兩個服務器,后端不用編寫任何前端代碼
前后端不分離,模板渲染,一個服務器,后端可能要編寫前端代碼
認識RESTful
在前后端分離的應用模式里,API接口如何定義?
例如對于后端數據庫中保存了商品的信息,前端可能需要對商品數據進行增刪改查,那相應的每個操作后端都需要提供一個API接口:
- POST /add-goods 增加商品
- POST /delete-goods 刪除商品
- POST /update-goods 修改商品
- GET /get-goods 查詢商品信息
對于接口的請求方式與路徑,每個后端開發人員可能都有自己的定義方式,風格迥異。
是否存在一種統一的定義方式,被廣大開發人員接受認可的方式呢?
這就是被普遍采用的API的RESTful設計風格。
RESTful設計方法
RESTful 用于規范接口的設計,來使廣大人員都可以接受
1. 域名
應該盡量將一類處理類似數據的API部署在專用域名之下。
比如說對手機操作的api 可以設置一個 shouji.jd.com 域名
https://api.example.com
如果確定API很簡單,不會有進一步擴展,可以考慮放在主域名下。
https://example.org/api/
2. 版本(Versioning)
應該將API的版本號放入URL。
http://www.example.com/app/1.0/foo
http://www.example.com/app/1.1/foo
http://www.example.com/app/2.0/foo
另一種做法是,將版本號放在HTTP頭信息中,但不如放入URL方便和直觀。Github采用這種做法。
因為不同的版本,可以理解成同一種資源的不同表現形式,所以應該采用同一個URL。版本號可以在HTTP請求頭信息的Accept字段中進行區分(參見Versioning REST Services):
Accept: vnd.example-com.foo+json; version=1.0
Accept: vnd.example-com.foo+json; version=1.1
Accept: vnd.example-com.foo+json; version=2.0
3. 路徑(Endpoint)
路徑又稱"終點"(endpoint),表示API的具體網址,每個網址代表一種資源(resource)
(1) 資源作為網址,只能有名詞,不能有動詞,而且所用的名詞往往與數據庫的表名對應。
舉例來說,以下是不好的例子:
/getProducts
/listOrders
/retreiveClientByOrder?orderId=1
對于一個簡潔結構,你應該始終用名詞。 此外,利用的HTTP方法可以分離網址中的資源名稱的操作。
GET /products :將返回所有產品清單
POST /products :將產品新建到集合
GET /products/4 :將獲取產品 4
PATCH(或)PUT /products/4 :將更新產品 4
(2) API中的名詞應該使用復數。無論子資源或者所有資源。
舉例來說,獲取產品的API可以這樣定義
獲取單個產品:http://127.0.0.1:8080/AppName/rest/products/1
獲取所有產品: http://127.0.0.1:8080/AppName/rest/products
最后的數字表示操作數據的id
3. HTTP動詞
對于資源的具體操作類型,由HTTP動詞表示。
常用的HTTP動詞有下面四個(括號里是對應的SQL命令)。
- GET(SELECT):從服務器取出資源(一項或多項)。
- POST(CREATE):在服務器新建一個資源。
- PUT(UPDATE):在服務器更新資源(客戶端提供改變后的完整資源)。
- DELETE(DELETE):從服務器刪除資源。
還有三個不常用的HTTP動詞。
- PATCH(UPDATE):在服務器更新(更新)資源(客戶端提供改變的屬性)。
- HEAD:獲取資源的元數據。
- OPTIONS:獲取信息,關于資源的哪些屬性是客戶端可以改變的。
下面是一些例子。
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:刪除某個指定動物園的指定動物
動物園的指定動物
4. 過濾信息(Filtering)
如果記錄數量很多,服務器不可能都將它們返回給用戶。API應該提供參數,過濾返回結果。
下面是一些常見的參數。
?limit=10:指定返回記錄的數量
?offset=10:指定返回記錄的開始位置。
?page=2&per_page=100:指定第幾頁,以及每頁的記錄數。
?sortby=name&order=asc:指定返回結果按照哪個屬性排序,以及排序順序。
?animal_type_id=1:指定篩選條件
參數的設計允許存在冗余,即允許API路徑和URL參數偶爾有重復。比如,GET /zoos/ID/animals 與 GET /animals?zoo_id=ID 的含義是相同的。
6. 狀態碼(Status Codes)
服務器向用戶返回的狀態碼和提示信息,常見的有以下一些(方括號中是該狀態碼對應的HTTP動詞)。
- 200 OK - [GET]:服務器成功返回用戶請求的數據
- 201 CREATED - [POST/PUT/PATCH]:用戶新建或修改數據成功。
- 202 Accepted - [*]:表示一個請求已經進入后臺排隊(異步任務)
- 204 NO CONTENT - [DELETE]:用戶刪除數據成功。
- 更多情況直接返回200
- 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 - [*]:服務器發生錯誤,用戶將無法判斷發出的請求是否成功。
狀態碼的完全列表參見這里或這里。
記住 200 401 403 404 500
7. 錯誤處理(Error handling)
如果狀態碼是4xx,服務器就應該向用戶返回出錯信息。一般來說,返回的信息中將error作為鍵名,出錯信息作為鍵值即可。
{
error: "Invalid API key"
}
8. 返回結果
針對不同操作,服務器向用戶返回的結果應該符合以下規范。
- GET /collection:返回資源對象的列表(數組)
- GET /collection/resource:返回單個資源對象
- POST /collection:返回新生成的資源對象
- PUT /collection/resource:返回完整的資源對象
- PATCH /collection/resource:返回完整的資源對象
- DELETE /collection/resource:返回一個空文檔
9. 其他
服務器返回的數據格式,應該盡量使用JSON,避免使用XML。
使用Django開發REST接口
前端人員可以通過這個寫ajax請求
我們以在Django框架中使用的圖書英雄案例來寫一套支持圖書數據增刪改查的REST API接口,來理解REST API的開發。
在這可以快速進行數據導入
在此案例中,前后端均發送JSON格式數據。
新增數據的時候,傳遞的參數根據模型類設計,沒有默認值,沒有允許為空一般就是必傳的字段
最外層是列表的,想返回json數據的,要添加:JsonResponse(book_list, safe=False)
status為返回的狀態碼
返回創建后的結果
發送json數據一定要使用雙引號
發送的時候會出現csrf認證失敗,先注銷掉csrf機制
# views.py
from datetime import datetime
class BooksAPIVIew(View):
"""
查詢所有圖書、增加圖書
"""
def get(self, request):
"""
查詢所有圖書
路由:GET /books/
"""
queryset = BookInfo.objects.all()
book_list = []
for book in queryset:
book_list.append({
'id': book.id,
'btitle': book.btitle,
'bpub_date': book.bpub_date,
'bread': book.bread,
'bcomment': book.bcomment,
'image': book.image.url if book.image else ''
})
return JsonResponse(book_list, safe=False)
def post(self, request):
"""
新增圖書
路由:POST /books/
"""
json_bytes = request.body
json_str = json_bytes.decode()
book_dict = json.loads(json_str)
# 此處詳細的校驗參數省略
book = BookInfo.objects.create(
btitle=book_dict.get('btitle'),
bpub_date=datetime.strptime(book_dict.get('bpub_date'), '%Y-%m-%d').date()
)
return JsonResponse({
'id': book.id,
'btitle': book.btitle,
'bpub_date': book.bpub_date,
'bread': book.bread,
'bcomment': book.bcomment,
'image': book.image.url if book.image else ''
}, status=201)
class BookAPIView(View):
def get(self, request, pk):
"""
獲取單個圖書信息
路由: GET /books/<pk>/
"""
try:
book = BookInfo.objects.get(pk=pk)
except BookInfo.DoesNotExist:
return HttpResponse(status=404)
return JsonResponse({
# 此段代碼直接進行復制
'id': book.id,
'btitle': book.btitle,
'bpub_date': book.bpub_date,
'bread': book.bread,
'bcomment': book.bcomment,
'image': book.image.url if book.image else ''
})
def put(self, request, pk):
"""
修改圖書信息
路由: PUT /books/<pk>
"""
# 更新數據的代碼
try:
book = BookInfo.objects.get(pk=pk)
except BookInfo.DoesNotExist:
return HttpResponse(status=404)
json_bytes = request.body
json_str = json_bytes.decode()
book_dict = json.loads(json_str)
# 此處詳細的校驗參數省略
book.btitle = book_dict.get('btitle')
book.bpub_date = datetime.strptime(book_dict.get('bpub_date'), '%Y-%m-%d').date()
book.save()
return JsonResponse({
'id': book.id,
'btitle': book.btitle,
'bpub_date': book.bpub_date,
'bread': book.bread,
'bcomment': book.bcomment,
'image': book.image.url if book.image else ''
})
def delete(self, request, pk):
"""
刪除圖書
路由: DELETE /books/<pk>/
"""
try:
book = BookInfo.objects.get(pk=pk)
except BookInfo.DoesNotExist:
return HttpResponse(status=404)
book.delete()
return HttpResponse(status=204)
# urls.py
urlpatterns = [
url(r'^books/$', views.BooksAPIVIew.as_view()),
url(r'^books/(?P<pk>\d+)/$', views.BookAPIView.as_view())
]
測試
使用Postman測試上述接口
1) 獲取所有圖書數據
GET 方式訪問 http://127.0.0.1:8000/books/, 返回狀態碼200,數據如下
[
{
"id": 1,
"btitle": "射雕英雄傳",
"bpub_date": "1980-05-01",
"bread": 12,
"bcomment": 34,
"image": ""
},
{
"id": 2,
"btitle": "天龍八部",
"bpub_date": "1986-07-24",
"bread": 36,
"bcomment": 40,
"image": ""
},
{
"id": 3,
"btitle": "笑傲江湖",
"bpub_date": "1995-12-24",
"bread": 20,
"bcomment": 80,
"image": ""
},
{
"id": 4,
"btitle": "雪山飛狐",
"bpub_date": "1987-11-11",
"bread": 58,
"bcomment": 24,
"image": ""
},
{
"id": 5,
"btitle": "西游記",
"bpub_date": "1988-01-01",
"bread": 10,
"bcomment": 10,
"image": "booktest/xiyouji.png"
},
{
"id": 6,
"btitle": "水滸傳",
"bpub_date": "1992-01-01",
"bread": 10,
"bcomment": 11,
"image": ""
},
{
"id": 7,
"btitle": "紅樓夢",
"bpub_date": "1990-01-01",
"bread": 0,
"bcomment": 0,
"image": ""
}
]
2)獲取單一圖書數據
GET 訪問 http://127.0.0.1:8000/books/5/ ,返回狀態碼200, 數據如下
{
"id": 5,
"btitle": "西游記",
"bpub_date": "1988-01-01",
"bread": 10,
"bcomment": 10,
"image": "booktest/xiyouji.png"
}
GET 訪問http://127.0.0.1:8000/books/100/,返回狀態碼404
3)新增圖書數據
POST 訪問http://127.0.0.1:8000/books/,發送JSON數據:
{
"btitle": "三國演義",
"bpub_date": "1990-02-03"
}
返回狀態碼201,數據如下
{
"id": 8,
"btitle": "三國演義",
"bpub_date": "1990-02-03",
"bread": 0,
"bcomment": 0,
"image": ""
}
4)修改圖書數據
PUT 訪問http://127.0.0.1:8000/books/8/,發送JSON數據:
{
"btitle": "三國演義(第二版)",
"bpub_date": "1990-02-03"
}
返回狀態碼200,數據如下
{
"id": 8,
"btitle": "三國演義(第二版)",
"bpub_date": "1990-02-03",
"bread": 0,
"bcomment": 0,
"image": ""
}
5)刪除圖書數據
DELETE 訪問http://127.0.0.1:8000/books/8/,返回204狀態碼
明確REST接口開發的核心任務
分析一下上節的案例,可以發現,在開發REST API接口時,視圖中做的最主要有三件事:
- 將請求的數據(如JSON格式)轉換為模型類對象
- 操作數據庫
- 將模型類對象轉換為響應的數據(如JSON格式)
序列化Serialization
這里是將字典列表等,轉換為json字符串,這里反序列化是將前端傳遞的數據轉換為數據對象的過程
維基百科中對于序列化的定義:
序列化(serialization)在計算機科學的資料處理中,是指將數據結構或物件狀態轉換成可取用格式(例如存成檔案,存于緩沖,或經由網絡中傳送),以留待后續在相同或另一臺計算機環境中,能恢復原先狀態的過程。依照序列化格式重新獲取字節的結果時,可以利用它來產生與原始物件相同語義的副本。對于許多物件,像是使用大量參照的復雜物件,這種序列化重建的過程并不容易。面向對象中的物件序列化,并不概括之前原始物件所關聯的函式。這種過程也稱為物件編組(marshalling)。從一系列字節提取數據結構的反向操作,是反序列化(也稱為解編組, deserialization, unmarshalling)。
序列化在計算機科學中通常有以下定義:
? 在數據儲存與傳送的部分是指將一個對象。這程序被應用在不同應用程序之間傳送對象),以及服務器將對象)儲存到檔案或數據庫。相反的過程又稱為反序列化。
簡而言之,我們可以將序列化理解為:
將程序中的一個數據結構類型轉換為其他格式(字典、JSON、XML等),例如將Django中的模型類對象裝換為JSON字符串,這個轉換過程我們稱為序列化。
如:
queryset = BookInfo.objects.all()
book_list = []
# 序列化
for book in queryset:
book_list.append({
'id': book.id,
'btitle': book.btitle,
'bpub_date': book.bpub_date,
'bread': book.bread,
'bcomment': book.bcomment,
'image': book.image.url if book.image else ''
})
return JsonResponse(book_list, safe=False)
反之,將其他格式(字典、JSON、XML等)轉換為程序中的數據,例如將JSON字符串轉換為Django中的模型類對象,這個過程我們稱為反序列化。
如:
json_bytes = request.body
json_str = json_bytes.decode()
# 反序列化
book_dict = json.loads(json_str)
book = BookInfo.objects.create(
btitle=book_dict.get('btitle'),
bpub_date=datetime.strptime(book_dict.get('bpub_date'), '%Y-%m-%d').date()
我們可以看到,在開發REST API時,視圖中要頻繁的進行序列化與反序列化的編寫。
總結
在開發REST API接口時,我們在視圖中需要做的最核心的事是:
-
將數據庫數據序列化為前端所需要的格式,并返回;
-
將前端發送的數據反序列化為模型類對象,并保存到數據庫中。
DjangoRESTframework簡介
- 在序列化與反序列化時,雖然操作的數據不盡相同,但是執行的過程卻是相似的,也就是說這部分代碼是可以復用簡化編寫的。
- 在開發REST API的視圖中,雖然每個視圖具體操作的數據不同,但增、刪、改、查的實現流程基本套路化,所以這部分代碼也是可以復用簡化編寫的:
- 增:校驗請求數據 -> 執行反序列化過程 -> 保存數據庫 -> 將保存的對象序列化并返回
- 刪:判斷要刪除的數據是否存在 -> 執行數據庫刪除
- 改:判斷要修改的數據是否存在 -> 校驗請求的數據 -> 執行反序列化過程 -> 保存數據庫 -> 將保存的對象序列化并返回
-
查:查詢數據庫 -> 將數據序列化并返回
Django REST framework可以幫助我們簡化上述兩部分的代碼編寫,大大提高REST API的開發速度。
認識Django REST framework
Django REST framework 框架是一個用于構建Web API 的強大而又靈活的工具。
通常簡稱為DRF框架 或 REST framework。
DRF框架是建立在Django框架基礎之上,由Tom Christie大牛二次開發的開源項目。
特點
- 提供了定義序列化器Serializer的方法,可以快速根據 Django ORM 或者其它庫自動序列化/反序列化;
- 提供了豐富的類視圖、Mixin擴展類,簡化視圖的編寫;
- 豐富的定制層級:函數視圖、類視圖、視圖集合到自動生成 API,滿足各種需要;
- 多種身份認證和權限認證方式的支持;
- 內置了限流系統;
- 直觀的 API web 界面;
- 可擴展性,插件豐富
資料:
- 官方文檔
- Github源碼
筆記
-
后臺主要是給運營人員用,主要用來增加商品信息,運營好像不會編程,要提供圖形化界面,后臺的前端頁面已經寫完了,需要我們補充后端邏輯
-
前后端不分離需要后端模板渲染,前后端分離在進行數據加載的時候統一返回json數據,由前端進行渲染
-
美多商城是一個BtoC(企業對用戶)的模式,淘寶是CtoC的模式,還有BtoB模式,
-
電商類網站是在所有網站中包含功能最多的,會開發電商,開發其他網站就比較容易了,做新的網站時,可以借鑒美多商城的實現方案
-
-
-
這樣使用 127.0.0.1:8000 都可以進行訪問 -
這里修改前端html頁面時進行了手動服務器刷新
-
mounted 指向的函數是頁面加載完成之后執行的
-
有了接口,前端和后端就能進行代碼的編寫,接口是由后端人員進行定義的 -
前后端分離使用前端進行跳轉
這是跳轉代碼 -
前后端分離的結果由測試人員進行查看
-
可以使用postman自己進行測試,測試小組發現bug會發郵件給開發小組
-
-
后端的工作主要就是設計接口,寫后端邏輯
-
python manage.py 輸入后會顯示這個模塊的功能選項信息
-
創建完應用要進行注冊,否則可能沒法生成遷移文件或是使用模板
-
連接成功點擊apply -
函數參數內可放字典,數據之間允許有空白符,字典 k 冒號 v 之間允許有空白符 v 可以寫一個表達式,字典存放的是表達式計算的結果
-
-
postman 測試完成后,就可以給測試小組
-
字典通過 [],獲取參數,不存在的話會拋出異常,如果通過get方式,沒有取到數據的話會返回None
-
判斷數據是否為空 -
json.loads 返回的是字典
-
get不到東西會拋出異常 -
這是物理刪除
原文鏈接:https://blog.csdn.net/weixin_43297727/article/details/114891761
- 上一篇:React組件的生命周期函數
- 下一篇:Stream流水線的實現原理是什么
相關推薦
- 2022-06-27 Vscode的SSH插件遠程連接Linux的實現步驟_其它綜合
- 2022-09-03 Python流程控制if條件選擇與for循環_python
- 2022-05-02 C語言遞歸實現歸并排序詳解_C 語言
- 2022-04-23 通過CSS的sticky屬性 重新回顧 position
- 2022-07-06 Android中ViewFlipper和AdapterViewFlipper使用的方法實例_Andr
- 2022-05-06 python畫圖時給圖中的點加標簽和plt.text的使用_python
- 2022-09-26 docker已啟動容器修改添加端口映射的兩種方法_docker
- 2022-11-07 React組件封裝中三大核心屬性詳細介紹_React
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支