Description
Day 2, 13:55-14:25
Abstract
Django Validator 可以是資料在進入 DB 前最終的守門員,但難道我們必須走到最後一步(準備寫入 DB)才能發現外部傳來的資料沒有符合規範嗎? 若之前業務邏輯已觸碰髒資料而導致問題, 我們是否要在程式碼裡面加上許多錯誤處理? 這樣的問題在引入 DRF(Django Rest Framework)的 ModelSerializer 後會有解決辦法嗎? 我希望透過介紹 Django Validator/Serializer 的設計目的與使用時機, 並引出在公司專案內使用上的不便之處. 如果上述各種方法都有痛點, 那不如就自做一套 HTTP request validation 的機制吧 ! 想要達成哪些目標 ? 1. 夠簡單用, 且依賴低 2. 不只在 Django Framework 內可用, 單純 Python 函式參數驗證也能使用 3. 便於日後製作 API spec document 4. 容易擴展新的驗證條件 5. 盡可能 functional programming (純粹爽) 向各位介紹 HARDCORE - "SPEC"
Description
Django framework 一開始誕生的主要的目標是為了簡化以資料庫為驅動的網站開發, 實際使用上則是將架構分成 Template/View/Model 三大區塊並做直接的串接. 這也讓其設計的 Serializer 系統著重在不同資料類型(xml/ymal/json/python/queryset)的序列化轉換而忽略了驗證資料的合規性. 而其 Validator 的資料驗證系統只著重在 a) 資料進入資料庫欄位時的驗證 b) 將 Template 表單欄位輸入值轉換成 view 內變數時的驗證. 這產生了兩個問題 1) 無法針對 GET 使用的 query paramerter 進行驗證 2) Validator 必須搭配表單 (Form) 使用, 等同於各欄位設計必須基於 Field, 才能將 validator 植入 Field 的 default_validators list 內.
上述的使用情境與限制, 無法充分地滿足公司內部專案的需求, 現實的狀況是當專案爆長, 許多頁面開始利用前端框架進行渲染, template 上不一定會使用 form; 而後端更引入 DRF, 使得 API 的設計規範出現更多分歧(function v.s view class v.s. APIView-like), ModelSerializer 的出現導致工程師容易在 APIView 內濫用 serializer.is_valid 作為 API request 與資料進入 model 的唯一檢查手段. 久之, 當 view 裡面的商業邏輯越來越多, 但卻都希望勁量靠近圍繞著 serializer 來確保使用資料合乎規範, 程式碼耦合性變高, 越難職責切割, 開始容易疊床架屋...
幾經思考, 決定明確將 API 設計方向切成數個部分,
API 進入點部分 商業邏輯部分 API 返回值部分 第一部分: API 進入點. 這裡會有 HTTP request 透過 view 的 get/post function 被傳入, 我們可以在此處進行所有參數的驗證. 一來避免錯誤或惡意的參數在接下來的程式碼中被使用導致不預期的錯誤, 二來儘早參數驗證也能減少後面不必要的檢查程式碼.
第二部分: 商業邏輯部分 此處可以放心將參數拿出來使用, 並且專注在商業邏輯.
第三部分: 將上述產生的結果, 經由客製化的 response serializer 轉換成前端使用的資料格式.
這裡可以看出另一個問題, DRF APIView 的設計只讓使用者指定一個 serializer class, 但其實這三個部分都有需要做類似的事情而且欄位可能不同. (題外話)
於是我們專注在第一部分, 將 View/APIView 的 HTTP method 介面做了分析, 概念上是將資料( request.GET/request.POST (Django View) 或是 request.query_params, request.data (DRF APIView)) 從 request 截出, 進入參數驗證流程, 將驗證結果(成功或失敗)收集起來, 並根據例外優先權來決定要回傳哪個錯誤. 更多細節介紹經會透過投影片, 用程式碼的方式來解講.
Slides not uploaded by the speaker. HackMD: https://hackmd.io/@pycontw/2021/%2F%40pycontw%2FH1d5N45Mt
Speaker: Kilik Kuo
改變自己先, 影響世界後. https://www.linkedin.com/in/kilik-kuo-3923a454/