day71:drf:API介面&Restful API規範&Django Rest Framework&drf中的序列化和反序列化功能

Poke發表於2020-10-20

目錄

1.web應用模式

2.API介面

3.Restful API規範

4.序列化

5.Django Rest Framework

  1.drf的簡單介紹

  2.drf的特點

  3.如何安裝drf

  4.drf的強大之處

  5.序列化器的使用

1.web應用模式

在開發Web應用中,有兩種應用模式:

1.前後端不分離

2.前後端分離

2.API介面

為了在團隊內部形成共識、防止個人習慣差異引起的混亂,我們需要找到一種大家都覺得很好的介面實現規範,而且這種規範能夠讓後端寫的介面,用途一目瞭然,減少雙方之間的合作成本。

目前市面上大部分公司開發人員使用的介面服務架構主要有:restful、rpc

rpc: 翻譯成中文:遠端過程呼叫[遠端服務呼叫]

但是介面多了,對應函式名和引數就多了,前端在請求api介面時,就會比較難找.容易出現重複的介面

restful: 翻譯成中文: 資源狀態轉換.

把後端所有的資料/檔案都看成資源.

那麼介面請求資料,本質上來說就是對資源的操作了.

web專案中操作資源,無非就是增刪查改.所以要求在位址列中宣告要操作的資源是什麼,然後通過http請求動詞來說明對資源進行哪一種操作.

例如:

  POST http://www.lufei.com/api/students/ 新增學生資料

  GET http://www.lufei.com/api/students/ 獲取所有學生

  DELETE http://www.lufei.com/api/students/<pk> 刪除1個學生

3. Restful API規範

REST全稱是Representational State Transfer,中文意思是表述(編者注:通常譯為表徵)性狀態轉移。 它首次出現在2000年Roy Fielding的博士論文中。

RESTful是一種定義Web API介面的設計風格,尤其適用於前後端分離的應用模式中。

這種風格的理念認為後端開發任務就是提供資料的,對外提供的是資料資源的訪問介面,所以在定義介面時,客戶端訪問的URL路徑就表示這種要操作的資料資源。

而對於資料資源分別使用POST、DELETE、GET、UPDATE等請求動作來表達對資料的增刪查改。

請求方法請求地址後端操作
GET /students 獲取所有學生
POST /students 增加學生
GET /students/<pk> 獲取編號為pk的學生
PUT /students/<pk> 修改編號為pk的學生
DELETE /students/<pk> 刪除編號為pk的學生

 

 

 

 

 

 

 

 

事實上,我們可以使用任何一個框架都可以實現符合restful規範的API介面。

參考文件:http://www.runoob.com/w3cnote/restful-architecture.html

restful API規範詳情

1. 域名

應該儘量將API部署在專用域名之下。

https://www.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

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應該提供引數,過濾返回結果。

下面是一些常見的引數。query_string 查詢字串,位址列後面問號後面的資料,格式: name=xx&sss=xxx

?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 的含義是相同的。

/zoos/2/animals

/animals?zoo_id=2

6. 狀態碼(Status Codes)

伺服器向使用者返回的狀態碼和提示資訊,常見的有以下一些(方括號中是該狀態碼對應的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 - [*]:伺服器發生錯誤,使用者將無法判斷髮出的請求是否成功。

狀態碼的完全列表參見這裡這裡

7. 錯誤處理(Error handling)

如果狀態碼是4xx,伺服器就應該向使用者返回出錯資訊。一般來說,返回的資訊中將error作為鍵名,出錯資訊作為鍵值即可。

{
    error: "Invalid API key"
}

8. 返回結果

針對不同操作,伺服器向使用者返回的結果應該符合以下規範。

  • GET /collections:返回資源物件的列表(陣列)

  • GET /collection/ID:返回單個資源物件(json)

  • POST /collection:返回新生成的資源物件(json)

  • PUT /collection/ID:返回完整的資源物件(json)

  • DELETE /collection/ID:返回一個空文件(空字串)

9. 超媒體(Hypermedia API)

RESTful API最好做到Hypermedia(即返回結果中提供連結,連向其他API方法),使得使用者不查文件,也知道下一步應該做什麼。

比如,Github的API就是這種設計,訪問api.github.com會得到一個所有可用API的網址列表。

{
"current_user_url": "https://api.github.com/user",
"authorizations_url": "https://api.github.com/authorizations",
// ...
}

從上面可以看到,如果想獲取當前使用者的資訊,應該去訪問api.github.com/user,然後就得到了下面結果。

{
  "message": "Requires authentication",
  "documentation_url": "https://developer.github.com/v3"
}

上面程式碼表示,伺服器給出了提示資訊,以及文件的網址。

10. 其他

伺服器返回的資料格式,應該儘量使用JSON,避免使用XML。

4.序列化

api介面開發,最核心最常見的一個過程就是序列化,所謂序列化就是把資料轉換格式,序列化可以分兩個階段:

序列化: 把我們識別的資料轉換成指定的格式提供給別人

例如:我們在django中獲取到的資料預設是模型物件,但是模型物件資料無法直接提供給前端或別的平臺使用,所以我們需要把資料進行序列化,變成字串或者json資料,提供給別人。

反序列化:把別人提供的資料轉換/還原成我們需要的格式

例如:前端js提供過來的json資料,對於python而言就是字串,我們需要進行反序列化換成模型類物件,這樣我們才能把資料儲存到資料庫中。

5.Django Rest Framework

1.drf的簡單介紹

核心思想: 縮減編寫api介面的程式碼量 -- DRF

Django REST framework是一個建立在Django基礎之上的Web 應用開發框架,可以快速的開發REST API介面應用。在REST framework中,提供了序列化器Serialzier的定義,可以幫助我們簡化序列化與反序列化的過程,不僅如此,還提供豐富的類檢視、擴充套件類、檢視集來簡化檢視的編寫工作。REST framework還提供了認證、許可權、限流、過濾、分頁、介面文件等功能支援。REST framework提供了一個API 的Web視覺化介面來方便檢視測試介面。

中文文件:https://q1mi.github.io/Django-REST-framework-documentation/#django-rest-framework

github: https://github.com/encode/django-rest-framework/tree/master

英文文件:https://www.django-rest-framework.org/

2.drf的特點

  • 提供了定義序列化器Serializer的方法,可以快速根據 Django ORM 或者其它庫自動序列化/反序列化

  • 提供了豐富的類檢視、Mixin擴充套件類,簡化檢視的編寫

  • 豐富的定製層級:函式檢視、類檢視、檢視集合到自動生成 API,滿足各種需要;

  • 多種身份認證和許可權認證方式的支援;[jwt]

  • 內建了限流系統;

  • 直觀的 API web 介面;

  • 可擴充套件性,外掛豐富

3.如何安裝drf

DRF需要以下依賴:

  • Python (2.7, 3.2, 3.3, 3.4, 3.5, 3.6)

  • Django (1.10, 1.11, 2.0)

DRF是以Django擴充套件應用的方式提供的,所以我們可以直接利用已有的Django環境而無需從新建立。(若沒有Django環境,需要先建立環境安裝Django)

1.安裝drf

pip3 install django==2.2 # 安裝django2 不要安裝django1
pip3 install djangorestframework # 安裝drf

2.建立一個django專案

django-admin startproject drfdemo

3.新增rest_framework應用

settings.pyINSTALLED_APPS中新增'rest_framework'。

INSTALLED_APPS = [
    ...
    'rest_framework',
]

接下來就可以使用DRF提供的功能進行api介面開發了。在專案中如果使用rest_framework框架實現API介面,主要有以下三個步驟:

  • 將請求的資料(如JSON格式)轉換為模型類物件

  • 運算元據庫

  • 將模型類物件轉換為響應的資料(如JSON格式)

4.drf的強大之處

下面將會告訴你用drf寫程式碼的完整步驟:

0.建立模型操作類:students/models.py

建立一個app,名字為students

class Student(models.Model):
    # 模型欄位
    name = models.CharField(max_length=100,verbose_name="姓名",help_text='提示文字:不能為空')
    sex = models.BooleanField(default=1,verbose_name="性別")
    age = models.IntegerField(verbose_name="年齡")
    class_null = models.CharField(max_length=5,verbose_name="班級編號")
    description = models.TextField(max_length=1000,verbose_name="個性簽名")

    class Meta:
        db_table="tb_student" # 將表名改為tb_student
        verbose_name = "學生"
        verbose_name_plural = verbose_name

1.執行準備工作

1.將student元件新增到settings.py中的INSTALLAPP中

2.安裝pymysql,執行資料庫連線

pip install pymysql

3.在專案目錄下的__init__.py中使用pymysql作為資料庫驅動

import pymysql
pymysql.install_as_MySQLdb()

4.settings.py中設定資料庫的相關配置

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': "students",
        "HOST": "127.0.0.1",
        "PORT": 3306,
        "USER": "root",
        "PASSWORD":"123",
    }
}

5.在終端下執行資料庫遷移

python manage.py makemigrations
python manage.py migrate

6.在執行 python manage.py makemigrations會報出如下錯誤

解決方法:

註釋掉 python/site-packages/django/backends/mysql/base.py中的35和36行程式碼。

7.在執行 python manage.py migrate會報如下錯誤

解決方法:

backends/mysql/operations.py146行裡面新增一個行程式碼:

2.建立序列化器 students/serializers.py

在students應用目錄中新建serializers.py用於儲存該應用的序列化器。

建立一個StudentModelSerializer用於序列化與反序列化。

# 建立序列化器類,回頭會在試圖中被呼叫
class StudentModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = Student
        fields = "__all__"
  • model 指明該序列化器處理的資料欄位從模型類Student參考生成

  • fields 指明該序列化器包含模型類中的哪些欄位,'all'指明包含所有欄位

3.編寫檢視集合函式 students/views.py

在students應用的views.py中建立檢視StudentViewSet,這是一個檢視集合。

from rest_framework.viewsets import ModelViewSet
from .models import Student
from .serializers import StudentModelSerializer

class StudentViewSet(ModelViewSet):
    queryset = Student.objects.all() 
    serializer_class = StudentModelSerializer

4.定義路由 students/urls.py

在students應用的urls.py中定義路由資訊。

from . import views
from rest_framework.routers import DefaultRouter

# 路由列表
urlpatterns = []

router = DefaultRouter()  # 可以處理檢視的路由器,自動通過檢視來生成增刪改查的url路徑

router.register('students', views.StudentViewSet)  #students是生成的url字首,名稱隨便寫, 向路由器中註冊檢視集

urlpatterns += router.urls  # 將路由器中的所以路由資訊追到到django的路由列表中

最後把students子應用中的路由檔案載入到總路由檔案中.

# drf01/urls.py
from django.contrib import admin
from django.urls import path,include

urlpatterns = [
    path('admin/', admin.site.urls),
    path("stu/",include("students.urls")),
]

5.執行測試

點選連結127.0.0.1:8000/stu/students 可以訪問獲取所有資料的介面

我們可以通過這個頁面來進行對資料的增刪改查

 

5.序列化器的使用

1.序列化功能

1.先建立一個app:sers

python manage.py startapp sers

2.我們已有了一個資料庫模型類students/Student,使用這個資料模型類即可。

3.如果我們想為這個模型類提供一個序列化器,可以定義如下:

a.在應用中建立一個py檔案,比如叫做serializers.py

from rest_framework import serializers
class StudentSerizlizer(serializers.Serializer):

    name = serializers.CharField()
    age = serializers.IntegerField()
    class_null = serializers.CharField()
    description = serializers.CharField()

b.在檢視sers/views.py中使用序列化功能

from django.shortcuts import render,HttpResponse
from django.http import JsonResponse
from students import models
from django.views import View
from .serializers import StudentSerizlizer

class StudentView(View):

    def get(self,request):
        
        # 查詢所有資料
        all = models.Student.objects.all() 
        
        # 查詢一條資料
        one = models.Student.objects.get(id=1)  
        
        # 多條記錄序列化必須加many=True引數
        '''serializer.data是列表裡有多個小字典的形式'''
        serializer = StudentSerizlizer(all,many=True)
        
        # 單條記錄序列化
        '''serializer.data是一個字典'''
        serializer = StudentSerizlizer(one)  #得到的結果為字典 data


        # 若要序列化非dict物件,需要新增safe=false引數
         # 若要顯示中文,需要新增ensure_ascii選項
        return JsonResponse(serializer.data,safe=False,json_dumps_params={'ensure_ascii':False})

注意:serializer不是隻能為資料庫模型類定義,也可以為非資料庫模型類的資料定義。serializer是獨立於資料庫之外的存在。

c.配置一下應用列表和資料庫

INSTALLED_APPS = [
    ...
    'students.apps.StudentsConfig',
    'rest_framework',
    'ser'
]
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'drf01',
        'HOST':'127.0.0.1',
        'PORT':3306,
        'USER':'root',
        'PASSWORD':'123456'
    }
}

2.反序列化功能

和序列化功能相同,在django中建立一個反序列化功能的app

然後在app下的建立一個檔案serializer.py

from rest_framework import serializers

# 自定義校驗函式
def check666(val):
    if '666' in val:
        raise serializers.ValidationError('不能光喊6666啊,沒有用')
    else:
        return val

class StudentSerizlizer(serializers.Serializer):

    # validatior代表檢查name時,會通過一遍check666函式
    name = serializers.CharField(max_length=4,validators=[check666,])
    age = serializers.IntegerField(max_value=18)
    class_null = serializers.CharField()
    
    # required=False,allow_null=True允許欄位為空,也就是不用傳遞過來這個data
    description = serializers.CharField(required=False,allow_null=True)
    
    # allow_blank=True 允許只為空字串 
    description = serializers.CharField(allow_blank=True)

在檢視中使用反序列化功能

在反序列化中,主要的就是校驗功能

class Student(View):
    def get(self,request):
        ...
        
    def post(self,request):
        '''
             content-type :客戶端實際返回的內容的內容型別
            if content-type == 'urlencoded':
                 #username=chao&password=123&a=1
                 request.POST['username'] = 'chao'
              elif content-type == 'form-data':
                 # 分片接受資料
                 request.FILES
             
        django沒有自帶解析json資料的解析器,所以需要我們手動的解析,但是drf中已經有了
        content - type == 'application/json'
        '''
        print(request.POST)
        recv_data = {
            'name':request.POST.get('name'),
            'age':request.POST.get('age'),
            'class_null':request.POST.get('class_null'),
            'description':request.POST.get('description'),
        }

        ser = StudentSerizlizer(data=recv_data)
        print(ser.is_valid()) # 校驗,全部通過得到True,一個欄位錯了都是得到False
        print(ser.errors) # 所有欄位的錯誤資訊

        return HttpResponse('ok')

3.在序列化功能和反序列化功能的serializer.py中我們會經常用到一些欄位和引數

常用欄位:

欄位欄位構造方式
BooleanField BooleanField()
NullBooleanField NullBooleanField()
CharField CharField(max_length=None, min_length=None, allow_blank=False, trim_whitespace=True)
EmailField EmailField(max_length=None, min_length=None, allow_blank=False)
RegexField RegexField(regex, max_length=None, min_length=None, allow_blank=False)
SlugField SlugField(maxlength=50, min_length=None, allow_blank=False) 正則欄位,驗證正則模式 [a-zA-Z0-9-]+
URLField URLField(max_length=200, min_length=None, allow_blank=False)
UUIDField UUIDField(format='hex_verbose') format: 1) 'hex_verbose'"5ce0e9a5-5ffa-654b-cee0-1238041fb31a" 2) 'hex'"5ce0e9a55ffa654bcee01238041fb31a" 3)'int' - 如: "123456789012312313134124512351145145114" 4)'urn' 如: "urn:uuid:5ce0e9a5-5ffa-654b-cee0-1238041fb31a" 微軟時間戳,通過微秒生成一個隨機字串
IPAddressField IPAddressField(protocol='both', unpack_ipv4=False, **options)
IntegerField IntegerField(max_value=None, min_value=None)
FloatField FloatField(max_value=None, min_value=None)
DecimalField DecimalField(max_digits, decimal_places, coerce_to_string=None, max_value=None, min_value=None) max_digits: 最多位數 decimal_palces: 小數點位置
DateTimeField DateTimeField(format=api_settings.DATETIME_FORMAT, input_formats=None)
DateField DateField(format=api_settings.DATE_FORMAT, input_formats=None)
TimeField TimeField(format=api_settings.TIME_FORMAT, input_formats=None)
DurationField DurationField()
ChoiceField ChoiceField(choices) choices與Django的用法相同
MultipleChoiceField MultipleChoiceField(choices)
FileField FileField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)
ImageField ImageField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)
ListField ListField(child=, min_length=None, max_length=None)
DictField DictField(child=)

 

選項引數

引數名稱作用
max_length 最大長度
min_length 最小長度
allow_blank 是否允許為空
trim_whitespace 是否截斷空白字元
max_value 最大值
min_value 最小值

 

 

 

 

 

 

 

 

 

通用引數

引數名稱說明
read_only 表明該欄位僅用於序列化輸出,預設False
write_only 表明該欄位僅用於反序列化輸入,預設False
required 表明該欄位在反序列化時必須輸入,預設True
default 反序列化時使用的預設值
allow_null 表明該欄位是否允許傳入None,預設False
validators 該欄位使用的驗證器
error_messages 包含錯誤編號與錯誤資訊的字典
label 用於HTML展示API頁面時,顯示的欄位名稱
help_text 用於HTML展示API頁面時,顯示的欄位幫助提示資訊

相關文章