Django實現統一包裝介面返回值資料格式

程式設計實驗室發表於2022-04-04

前言

最近實在太忙了,開始了一個新的專案,為了快速形成產品,我選擇了Django來實現後端,然後又拿起了之前我封裝了專案腳手架「DjangoStarter」

由於前段時間我寫了不少.NetCore的後端程式碼,對CRUD的介面開發又有了一些新的理解,所以肯定也要把DjangoStarter改造一番,改得更加順手~

題外話:話說我在前端終於真正用上react,不得不說 ts + react 真好用,「前端帶師」的安利可真不錯啊(妙啊

正題

準確來講是搭配DjangoRestFramework來實現的,核心程式碼分為兩部分,一個是錯誤處理包裝,一個是響應包裝renderer

無論請求是否報錯,都會被我們自定義的renderer包裝。

程式碼

開始上程式碼吧~

首先在專案裡新增一個新的Python包,位置根據情況自行安排,我在DjangoStarter模板中的位置是utils/response,接下來的兩個檔案都會放在這個目錄中

第一個是錯誤處理的exception.py檔案程式碼

from typing import Dict

from rest_framework.exceptions import ValidationError
from rest_framework.views import exception_handler
from rest_framework.views import Response
from rest_framework.utils.serializer_helpers import ReturnDict
from rest_framework import status


def custom_handler(err: ValidationError, context: dict):
    # 先呼叫REST framework預設的異常處理方法獲得標準錯誤響應物件
    response: Response = exception_handler(err, context)

    if response is None:
        return Response({
            'message': f'伺服器錯誤:{err}'
        }, status=status.HTTP_500_INTERNAL_SERVER_ERROR, exception=True)

    else:
        res = {'message': response.reason_phrase}
        res.update(response.data)
        return Response(res, status=response.status_code, exception=True)

然後是自定義renderer.py程式碼:

from rest_framework.renderers import JSONRenderer


class CustomRenderer(JSONRenderer):
    # 重構render方法
    def render(self, data, accepted_media_type=None, renderer_context=None):
        if renderer_context:
            # 響應的資訊,成功和錯誤的都是這個
            # 成功和異常響應的資訊,異常資訊在前面自定義異常處理中已經處理為{'message': 'error'}這種格式

            # 如果返回的data為字典
            if isinstance(data, dict):
                # 響應資訊中有message和code這兩個key,則獲取響應資訊中的message和code,並且將原本data中的這兩個key刪除,放在自定義響應資訊裡
                # 響應資訊中沒有則將msg內容改為請求成功 code改為請求的狀態碼
                msg = data.pop('message', '請求成功')
                code = data.pop('code', renderer_context["response"].status_code)
            # 如果不是字典則將msg內容改為請求成功 code改為響應的狀態碼
            else:
                msg = '請求成功'
                code = renderer_context["response"].status_code

            # 自定義返回的格式
            ret = {
                'message': msg,
                'code': code,
                'data': data,
            }
            # 返回JSON資料
            return super().render(ret, accepted_media_type, renderer_context)
        else:
            return super().render(data, accepted_media_type, renderer_context)

關鍵的地方都註釋了,很容易看懂

包裝出來每個介面的返回值就都是這個樣子

{
    "message": "msg",
    "code": 200,
    "data": {
        "data1": "123"
    }
}

配置

為了讓上面的程式碼發揮作用,還得配置一下RestFramework

編輯settings.py檔案,在REST_FRAMEWORK節點新增這兩個配置,注意路徑要和前面兩個檔案的路徑一致。這裡我是以DjangoStarter專案為例。

REST_FRAMEWORK = {
    # 全域性配置異常模組
    'EXCEPTION_HANDLER': 'utils.response.exception.custom_handler',
    # 修改預設返回JSON的renderer的類
    'DEFAULT_RENDERER_CLASSES': ('utils.response.renderer.CustomRenderer',),
}

現在就完成了~ 就是這麼簡單。

(比AspNetCore包裝返回值簡單……)

完整程式碼可以在GitHub上檢視:https://github.com/Deali-Axy/DjangoStarter

參考資料

相關文章