本節目錄
預備知識:django的CBV和FBV
CBV(class based view):多用,簡單回顧一下
FBV(function based view):
CBV模式的簡單操作:來個登陸頁面吧
login.html文件內容如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="{% url 'login' %}" method="post"> {% csrf_token %} 用戶名: <input type="text" name="username"> 密碼: <input type="text" name="password"> <input type="submit"> </form> </body> </html>
url.py內容如下
from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^login/', views.LoginView.as_view(),name='login'), ]
views.py
from django.shortcuts import render,HttpResponse,redirect # Create your views here. from django.views import View class LoginView(View): def get(self,request): return render(request,'login.html') def post(self,request): return HttpResponse('post')
大家還記得CBV的這個視圖函數,為什么get請求就能找到類的get方法,post請求就能找到post方法,其內部有個dispatch方法來進行分發,這又怎么玩呢,看源碼啦,從哪里看呢?那里先執行,就從哪里看
views.LoginView.as_view()這個東西是不是先執行啊,url接收到請求,調用了它對不對,as_view()類方法,這個類方法給你返回了一個叫view的方法,就是說這個url對應這個一個view方法,當用戶訪問login頁面的時候是不是就是執行了view(request),大家進去看看源碼吧。
然后你就可以玩dispatch方法了,看代碼:
from django.shortcuts import render,HttpResponse,redirect # Create your views here. from django.views import View class LoginView(View): def dispatch(self, request, *args, **kwargs): print('something...') res = super().dispatch(request, *args, **kwargs) #注意,不用傳self,為什么呢,因為super已經幫你吧self放進去啦 print('someting....') return res def get(self,request): return render(request,'login.html') def post(self,request): return HttpResponse('post')
為什么要說它呢,因為后面咱們的drf學習,就要用它啦。
摘自:http://www.ruanyifeng.com/blog/2014/05/restful_api.html
1. 什么是RESTFUl
RESTful 是目前最流行的 API 設計規范,用於 Web 數據接口的設計。
REST與技術無關,代表的是一種軟件架構風格,REST是Representational State Transfer的簡稱,中文翻譯為“表征狀態轉移”
REST從資源的角度類審視整個網絡,它將分布在網絡中某個節點的資源通過URL進行標識,客戶端應用通過URL來獲取資源的表征,獲得這些表征致使這些應用轉變狀態
所有的數據,不過是通過網絡獲取的還是操作(增刪改查)的數據,都是資源,將一切數據視為資源是REST區別與其他架構風格的最本質屬性
對於REST這種面向資源的架構風格,有人提出一種全新的結構理念,即:面向資源架構(ROA:Resource Oriented Architecture)
另外還有其他兩種,簡單了解。

遠程過程調用(RPC) 遠程過程調用為 Web 服務提供一個分布式函數/方法接口供用戶調用。這是一種較傳統的方式。通常,在 WSDL 中對 RPC 接口進行定義(類似於早期的XML-RPC)。本質上,RPC 方式利用一個簡單映射,把用戶請求直接轉化成一個特定語言編寫的函數/方法。現在,該方式已不再使用。 面向服務架構(SOA) 面向服務架構現在,業界比較關注的是遵從面向服務架構(Service-oriented architecture,SOA)來構建 Web 服務。該方式中,通訊是由消息驅動,而不再是某個動作(方法調用)。這種 Web 服務也稱為“面向消息的服務”。
網絡應用程序,分為前端和后端兩個部分。當前的發展趨勢,就是前端設備層出不窮(手機、平板、桌面電腦、其他專用設備......)。
因此,必須有一種統一的機制,方便不同的前端設備與后端進行通信。這導致API構架的流行,甚至出現"API First"的設計思想。RESTful API是目前比較成熟的一套互聯網應用程序的API設計理論。還有一篇《理解RESTful架構》,探討如何理解這個概念。
表征狀態轉移大概圖解:
2.RESTFUl API設計
2.1 使用協議
API與用戶的通信協議,總是使用HTTPs協議。
2.2 使用域名
應該盡量將API部署在專用域名之下,意思就是給API專門做一個服務器。
https://api.example.com
如果確定API很簡單,不會有進一步擴展,可以考慮放在主域名下。
https://example.org/api/
2.3 版本提示
網站的API可能一直在更新,那么應該將API的版本號放入URL。
https://api.example.com/v1/
另一種做法是,將版本號放在HTTP頭信息中,但不如放入URL方便和直觀。Github采用這種做法。
2.4 路徑寫法
路徑又稱"終點"(endpoint),表示API的具體網址。
在RESTful架構中,每個網址代表一種資源(resource),所以網址中不能有動詞,只能有名詞,而且所用的名詞往往與數據庫的表格名對應。一般來說,數據庫中的表都是同種記錄的"集合"(collection),所以API中的名詞也應該使用復數。
舉例來說,有一個API提供動物園(zoo)的信息,還包括各種動物和雇員的信息,則它的路徑應該設計成下面這樣。
https://api.example.com/v1/zoos https://api.example.com/v1/animals https://api.example.com/v1/employees
2.5 HTTP動詞
對於資源的具體操作類型,由HTTP動詞表示,請求方式時動詞,我們后端基於請求方式來分發對應的視圖函數來進行邏輯處理和數據處理、提取、加工等操作,但是URL中不能出現動詞。
常用的HTTP動詞有下面五個(括號里是對應的SQL命令)。
GET(SELECT):從服務器取出資源(一項或多項)。
POST(CREATE):在服務器新建一個資源。
PUT(UPDATE):在服務器更新資源(客戶端提供改變后的完整資源)。
PATCH(UPDATE):在服務器更新資源(客戶端提供改變的屬性,更新部分資源的意思)。他和put用哪個都可以,沒有太大的區別,我們用put方式偏多
DELETE(DELETE):從服務器刪除資源。
還有兩個不常用的HTTP動詞。
HEAD:獲取資源的元數據。
OPTIONS:獲取信息,關於資源的哪些屬性是客戶端可以改變的
下面是一些例子。RESTful 的核心思想就是,客戶端發出的數據操作指令都是"動詞 + 賓語"的結構。比如,GET /articles
這個命令,GET
是動詞,/articles
是賓語。根據 HTTP 規范,動詞一律大寫。
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:刪除某個指定動物園的指定動物
動詞覆蓋:
有些客戶端只能使用GET
和POST
這兩種方法。服務器必須接受POST
模擬其他三個方法(PUT
、PATCH
、DELETE
)。
這時,客戶端發出的 HTTP 請求,要加上X-HTTP-Method-Override
屬性,告訴服務器應該使用哪一個動詞,覆蓋POST
方法。
POST /api/Person/4 HTTP/1.1
X-HTTP-Method-Override: PUT
上面代碼中,X-HTTP-Method-Override
指定本次請求的方法是PUT
,而不是POST
。
賓語必須是名字:
賓語就是 API 的 URL,是 HTTP 動詞作用的對象。它應該是名詞,不能是動詞。比如,/articles
這個 URL 就是正確的,而下面的 URL 不是名詞,所以都是錯誤的。
/getAllCars /createNewCar /deleteAllRedCars
既然 URL 是名詞,那么應該使用復數,還是單數?
這沒有統一的規定,但是常見的操作是讀取一個集合,比如GET /articles
(讀取所有文章),這里明顯應該是復數。
為了統一起見,建議都使用復數 URL,比如GET /articles/2
要好於GET /article/2
。
2.6 過濾信息(filtering,或稱查詢參數)
如果記錄數量很多,服務器不可能都將它們返回給用戶。API應該提供參數,過濾返回結果。
下面是一些常見的參數。
?limit=10:指定返回記錄的數量 ?offset=10:指定返回記錄的開始位置。 ?page=2&per_page=100:指定第幾頁,以及每頁的記錄數。 ?sortby=name&order=asc:指定返回結果按照哪個屬性排序,以及排序順序。 ?animal_type_id=1:指定篩選條件
參數的設計允許存在冗余,即允許API路徑和URL參數偶爾有重復。比如,GET /zoo/ID/animals 與 GET /animals?zoo_id=ID 的含義是相同的。
常見的情況是,資源需要多級分類,因此很容易寫出多級的 URL,比如獲取某個作者的某一類文章。
GET /authors/12/categories/2
這種 URL 不利於擴展,語義也不明確,往往要想一會,才能明白含義。
更好的做法是,除了第一級,其他級別都用查詢字符串表達。
GET /authors/12?categories=2
下面是另一個例子,查詢已發布的文章。你可能會設計成下面的 URL。
GET /articles/published
查詢字符串的寫法明顯更好
GET /articles?published=true
2.7 狀態碼
2.7.1 狀態碼必須精確
客戶端的每一次請求,服務器都必須給出回應。回應包括 HTTP 狀態碼和數據兩部分。
HTTP 狀態碼就是一個三位數,分成五個類別。
1xx:相關信息
2xx:操作成功
3xx:重定向
4xx:客戶端錯誤
5xx:服務器錯誤
這五大類總共包含100多種狀態碼,覆蓋了絕大部分可能遇到的情況。每一種狀態碼都有標准的(或者約定的)解釋,客戶端只需查看狀態碼,就可以判斷出發生了什么情況,所以服務器應該返回盡可能精確的狀態碼。
API 不需要1xx
狀態碼,下面介紹其他四類狀態碼的精確含義。
2.7.2 2xx狀態碼
200
狀態碼表示操作成功,但是不同的方法可以返回更精確的狀態碼。
GET: 200 OK POST: 201 Created PUT: 200 OK PATCH: 200 OK DELETE: 204 No Content
上面代碼中,POST
返回201
狀態碼,表示生成了新的資源;DELETE
返回204
狀態碼,表示資源已經不存在。
此外,202 Accepted
狀態碼表示服務器已經收到請求,但還未進行處理,會在未來再處理,通常用於異步操作。下面是一個例子。
HTTP/1.1 202 Accepted { "task": { "href": "/api/company/job-management/jobs/2130040", "id": "2130040" } }
2.7.3 3xx狀態碼
API 用不到301
狀態碼(永久重定向)和302
狀態碼(暫時重定向,307
也是這個含義),因為它們可以由應用級別返回,瀏覽器會直接跳轉,API 級別可以不考慮這兩種情況。
API 用到的3xx
狀態碼,主要是303 See Other
,表示參考另一個 URL。它與302
和307
的含義一樣,也是"暫時重定向",區別在於302
和307
用於GET
請求,而303
用於POST
、PUT
和DELETE
請求。收到303
以后,瀏覽器不會自動跳轉,而會讓用戶自己決定下一步怎么辦。下面是一個例子。
HTTP/1.1 303 See Other
Location: /api/orders/12345
2.7.4 4xx狀態碼
4xx
狀態碼表示客戶端錯誤,主要有下面幾種。
400 Bad Request:服務器不理解客戶端的請求,未做任何處理。 401 Unauthorized:用戶未提供身份驗證憑據,或者沒有通過身份驗證。 403 Forbidden:用戶通過了身份驗證,但是不具有訪問資源所需的權限。 404 Not Found:所請求的資源不存在,或不可用。 405 Method Not Allowed:用戶已經通過身份驗證,但是所用的 HTTP 方法不在他的權限之內。 410 Gone:所請求的資源已從這個地址轉移,不再可用。 415 Unsupported Media Type:客戶端要求的返回格式不支持。比如,API 只能返回 JSON 格式,但是客戶端要求返回 XML 格式。 422 Unprocessable Entity :客戶端上傳的附件無法處理,導致請求失敗。 429 Too Many Requests:客戶端的請求次數超過限額。
2.7.5 5xx狀態碼
5xx
狀態碼表示服務端錯誤。一般來說,API 不會向用戶透露服務器的詳細信息,所以只要兩個狀態碼就夠了。
500 Internal Server Error:客戶端請求有效,服務器處理時發生了意外。
503 Service Unavailable:服務器無法處理請求,一般用於網站維護狀態。
總結一下常用狀態碼及對應的描述
200 OK - [GET]:服務器成功返回用戶請求的數據,該操作是冪等的(Idempotent)。 201 CREATED - [POST/PUT/PATCH]:用戶新建或修改數據成功。 202 Accepted - [*]:表示一個請求已經進入后台排隊(異步任務) 204 NO CONTENT - [DELETE]:用戶刪除數據成功。301
狀態碼(永久重定向)302
狀態碼(暫時重定向,307
也是這個含義) 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 - [*]:服務器發生錯誤,用戶將無法判斷發出的請求是否成功。 更多看這里:http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
2.8 服務器響應
2.8.1 響應數據格式
API 返回的數據格式,不應該是純文本,而應該是一個 JSON 對象,因為這樣才能返回標准的結構化數據。所以,服務器回應的 HTTP 頭的Content-Type
屬性要設為application/json
。
客戶端請求時,也要明確告訴服務器,可以接受 JSON 格式,即請求的 HTTP 頭的ACCEPT
屬性也要設成application/json
。下面是一個例子。
GET /orders/2 HTTP/1.1
Accept: application/json
2.8.2 發生錯誤時的響應
發生錯誤時不要響應200狀態碼,有一種不恰當的做法是,即使發生錯誤,也返回200
狀態碼,把錯誤信息放在數據體里面,就像下面這樣。
HTTP/1.1 200 OK Content-Type: application/json { "status": "failure", "data": { "error": "Expected at least two items in list." } }
上面代碼中,解析數據體以后,才能得知操作失敗。
這張做法實際上取消了狀態碼,這是完全不可取的。正確的做法是,狀態碼反映發生的錯誤,具體的錯誤信息放在數據體里面返回。下面是一個例子。
HTTP/1.1 400 Bad Request Content-Type: application/json { "error": "Invalid payoad.", "detail": { "surname": "This field is required." } }
2.8.3 響應結果
針對不同操作,服務器向用戶返回的結果應該符合以下規范。
GET /collection:返回資源對象的列表(數組),一般是[{"id":1,"name":"a",},{"id":2,name:"b"},]這種類型 GET /collection/resource:返回單個資源對象, 一般是查看的單條數據 {"id":1,"name":'a'} POST /collection:返回新生成的資源對象 , 一般是返回新添加的數據信息, 格式一般是{} PUT /collection/resource:返回完整的資源對象 一般時返回更新后的數據,{} PATCH /collection/resource:返回完整的資源對象 DELETE /collection/resource:返回一個空文檔 一般返回一個空字符串
例如:
2.9 Hypermedia API,提供鏈接
RESTful API最好做到Hypermedia,即返回結果中提供鏈接,API 的使用者未必知道,URL 是怎么設計的。一個解決方法就是,在回應中,給出相關鏈接,便於下一步操作。這樣的話,用戶只要記住一個 URL,就可以發現其他的 URL。這種方法叫做 HATEOAS。
舉例來說,GitHub 的 API 都在 api.github.com 這個域名。訪問它,就可以得到其他 URL。
{ ... "feeds_url": "https://api.github.com/feeds", "followers_url": "https://api.github.com/user/followers", "following_url": "https://api.github.com/user/following{/target}", "gists_url": "https://api.github.com/gists{/gist_id}", "hub_url": "https://api.github.com/hub", ... }
上面的回應中,挑一個 URL 訪問,又可以得到別的 URL。對於用戶來說,不需要記住 URL 設計,只要從 api.github.com 一步步查找就可以了。
HATEOAS 的格式沒有統一規定,上面例子中,GitHub 將它們與其他屬性放在一起。更好的做法應該是,將相關鏈接與其他屬性分開。
HTTP/1.1 200 OK Content-Type: application/json { "status": "In progress", "links": {[ { "rel":"cancel", "method": "delete", "href":"/api/status/12345" } , { "rel":"edit", "method": "put", "href":"/api/status/12345" } ]} }
再比如:當用戶向api.example.com的根目錄發出請求,會得到這樣一個文檔。
{"link": { "rel": "collection https://www.example.com/zoos", "href": "https://api.example.com/zoos", "title": "List of zoos", "type": "application/vnd.yourformat+json" }}
上面代碼表示,文檔中有一個links屬性,用戶讀取這個屬性就知道下一步該調用什么API了。rel表示這個API與當前網址的關系(collection關系,並給出該collection的網址),href表示API的路徑,title表示API的標題,type表示返回類型。
2.10 其他
(1)API的身份認證應該使用OAuth 2.0框架。
(2)服務器返回的數據格式,應該盡量使用JSON,避免使用XML。
drf是django發展來的一個符合restful接口規范的一個東西,啥東西呢,就是django的一個app,還記得app是啥不。DRF官網地址,但是大家記住一句話,即便是沒有這drf,我們照樣能做前后端分離的項目,自己做規范的數據接口,返回json數據,都是沒問題的昂,那為什么還用drf啊,這個更nb。
在官網中我們看一下這里:
首先下載安裝,django是必須要的,不過咱們的django已經下載好了,如果沒下載好,那么pip install django,執行一下:
pip install django pip install djangorestframework //執行這句話,下載drf # Set up a new project with a single application django-admin startproject tutorial . # Note the trailing '.' character cd tutorial django-admin startapp quickstart cd ..
1.先基於djangoCBV,不用DRF來寫個接口,看看效果
好,接下來我們創建一個django項目,models中創建一個表,添加一些數據,然后寫一個數據接口來獲取一下這些數據,返回json數據類型,按照我們CBV的模式來寫,但是下面還沒有用到我們的drf昂,只是告訴大家,沒有drf,你也能做。
views.py文件內容如下:
from django.shortcuts import render,HttpResponse,redirect import json # Create your views here. from django.views import View from app01 import models class CourseView(View): def get(self,request): #拿到queryset類型的數據,要加工成[{},{}]這種數據類型 course_obj_list = models.Course.objects.all() ret = [] for course_obj in course_obj_list: ret.append({ "title":course_obj.title, "desc":course_obj.desc, }) return HttpResponse(json.dumps(ret,ensure_ascii=False)) #ensure_ascii=False是告訴json不要對中文進行編碼,不然返回給前端的數據中如果有中文的話會被編碼成unicode類型的數據,導致前端看不到中文
urls.py內容如下:
from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ #url(r'^admin/', admin.site.urls), url(r'^courses/', views.CourseView.as_view(),name='courses'), #接口就寫好啦 ]
然后啟動項目,在瀏覽器中一訪問,就看到了我們后端返回的json數據:
有人就又說了,我們這么寫也ok啊,要drf干嘛,上面這個例子是個簡單的例子,數據簡單、邏輯簡單,你這樣寫當然看着沒有問題啦,但是數據量很大,結構很復雜的時候,你這樣寫的時候就頭疼了。
所以上面這個例子你就作為了解吧,我們玩一下drf。
玩DRF之前,我們先說一下我們DRF中有哪些內容:
咱們玩下面10個組件:
a.APIView (*****) b.序列化組件 (*****) c.試圖類(mixin) (*****) d.認證組件 (*****) e.權限組件 f.頻率組件 g.分頁組件 h.解析器組件 (*****) i.相應其組件 j.url控制器
2.基於DRF來寫接口
2.1 APIView組件
在我們的視圖中,通過CBV來寫視圖的時候,繼承APIView,url不變,還是上面那個,通過瀏覽器訪問,照樣能夠看到我們返回的數據,
views.py內容如下:
from django.shortcuts import render,HttpResponse,redirect import json from django.views import View from app01 import models #引入APIView,APIView是繼承的django的View,也就是APIView在View的基礎上添加了一些其他的功能 from rest_framework.views import APIView class CourseView(APIView): def get(self,request): course_obj_list = models.Course.objects.all() ret = [] for course_obj in course_obj_list: ret.append({ "title":course_obj.title, "desc":course_obj.desc, }) return HttpResponse(json.dumps(ret, ensure_ascii=False))
urls.py內容如下:
from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^courses/', views.CourseView.as_view(),name='courses'), ]
頁面訪問url效果:
照樣拿到了數據,那么怎么回事兒呢,我們看源碼,知道一下APIView的流程就行。
2.2 解析器組件
知識准備,還記得一個叫做contentType的http請求頭的東西嗎?回想一下。
后端根據contentType的類型來找到對應的解析數據的方法來解析數據,提取數據
但是django沒有內置的自動解開json數據類型的方法,那么只能去request.body里面拿原始的bytes類型的數據,然后自己解,其實很簡單,但是django沒有,可能是早先沒有考慮到。
django自動通過contentType來解析數據的那些方法就叫做django的解析器,能解的是urlencode和文件的那個mutipart/form-data類型的數據,然后將數據放到了request.POST方法里面。
def post(self,request): print(request.POST) print(type(request)) #通過這個對象的類型(類對象),找到它的源碼看看 return HttpResponse('POST')
而DRF通過自己的解析器,幫我們給request里面封裝了一個request.data屬性,獲取請求體里面的數據,然后解析,並且這個解析器基本上能夠解析所有的數據類型,包括django不能自動解析的json數據類型,我們通過Postman(關於Postman工具的使用,看下面那個章節)來調試一下,看看效果
def post(self,request): print(request.data) #打印結果是:{'name': 'chao'} return HttpResponse('POST')
我們接着往下看:首先我們給我們的試圖類添加一個類變量,這些類變量是用來控制我們視圖類里面的各個組件
看一下代碼:
from django.shortcuts import render,HttpResponse,redirect import json from django.views import View from app01 import models from rest_framework.views import APIView #導入解析器 from rest_framework.parsers import JSONParser,FormParser,MulTiPartParser # JSONParser:解析json數據的額 # FormParser:解析urlencoded數據的 # FileUploadParser:解析文件數據的 class CourseView(APIView): #寫一個類屬性,名字必須是parser_classes parser_classes = [JSONParser,] #里面存放我們上面導入的那幾個解析器,如果我們里面寫了一個JSONParser,那么解析器只能解析前端發送過來的json數據,存放到request.data里面,可以通過postman測試一下看看效果,為什么?看源碼吧 def get(self,request): course_obj_list = models.Course.objects.all() ret = [] for course_obj in course_obj_list: ret.append({ "title":course_obj.title, "desc":course_obj.desc, }) return HttpResponse(json.dumps(ret, ensure_ascii=False)) def post(self,request):
print('ok') #你會發現,即便是發送的數據類型不對,post方法也走了,但是request.data沒有東西,那么肯定是它出了問題 print(request.data) #request.data對我們的數據進行解析的,那么說明data不是一個變量,而是一個屬性方法,還記得屬性方法嗎 return HttpResponse('POST')
源碼看着比較復雜,這里我就不列舉了,反正你要知道的是,我們的解析器的查找使用順序是:
自己寫的類里面的parser_classes = [JSONParser,]---->然后找settings中的----->然后找默認的,只要找到,就用了。其他的組件也都是這么個順序,所以其他的咱們就不看了。
Postman是一個模擬發送請求並獲得響應結果的工具,不用這個工具的時候,我們寫web項目,調試接口返回數據的時候,是不是都要啟動項目,通過瀏覽器訪問,然后查看數據啊,有了這個工具我們就可以不用啟動瀏覽器來,通過這個工具就能進行調試,首先下載安裝
下載地址:https://www.getpostman.com/downloads/
安裝,然后使用,直接看圖吧,一看就明白: