Django實現統一包裝接口返回值數據格式

前言

最近實在太忙了,開始了一個新的項目,為了快速形成產品,我選擇了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上查看://github.com/Deali-Axy/DjangoStarter

參考資料