python restful 框架_restful-dj
restful-dj
基于 Django2/3 的 restful 自動路由框架。
此包解決的問題:
告別 Django 繁鎖的路由配置
更便捷的 restful 編碼體驗
自動解析/校驗請求參數,填充到路由處理函數
安裝
pip installrestful-dj
使用
此組件提供的包(package)名稱為 restful_dj,所有用到的模塊都在此包下引入。
示例:
from restful_dj import route
from enums import RouteTypes
@route('module_name', 'route_name', route_type = RouteTypes.TEST)
def test(req):
pass
裝飾器 @route 用于標記路由處理函數。RouteTypes 是自定義的路由數據。
restful-dj 包含以下幾個部分:
路由映射
為了避免在客戶端暴露代碼路徑,從設計上使用了映射的方式來處理請求。
中間件
在處理請求/響應過程中,可以對 request/response 以及其參數進行處理。
全局類型
在路由裝飾器的參數中包含的全局類型,如 RouteTypes
路由收集與持久化
為了提高線上性能的工具。
restful-dj 的使用流程如下:
注冊請求路徑前綴
在項目的根 urls.py 文件中,使用以下配置
from django.urls import path
import restful_dj
urlpatterns = [
path('any/prefix', restful_dj.dispatch)
]
其中,any/prefix 是用戶自定義的url前綴,可以使用空串 '',表示沒有特定前綴。
restful_dj.dispatch 中路由的分發入口。
注:可以通過部署地址 any/prefix 訪問接口列表 (僅在開發模式時可用),
如:http://localhost:8000/any/prefix 。
注冊路由映射
為了避免在客戶端暴露代碼路徑(同時避免意外訪問未授權的代碼),從設計上使用了映射的方式來處理請求。
注冊方式如下:
import restful_dj
restful_dj.map_routes({
'path.prefix': 'path.to',
})
path.prefix 為請求的路徑
path.to 為請求路徑時,應將其定向到的 python 包/模塊。
所有的路由目錄(頂層,不包含已經映射過目錄的子目錄)均需要被映射,未在映射表中的路徑請求,不會被處理。
restful-dj 會自動查找 包/模塊 path.to 下的所有路由。
路徑應為基于項目根目錄的相對路徑。
注冊中間件
中間件用于在處理請求/響應過程中,對 request/response 以及其參數進行處理。
注冊方式如下:
import restful_dj
from path.to import FooMiddleware
from path.to import BarMiddleware
restful_dj.register_middlewares(
FooMiddleware,
BarMiddleware,
)
當注冊了多個中間件時,它們會按被注冊的順序執行。
需要注意: 每一個中間件類型在程序運行期間共享一個實例。
如何開發中間件?參見 中間件類結構
編寫路由處理函數
路由文件位置沒有要求,只要配置好就可以了。
import restful_dj
restful_dj.map_routes({
'test': 'test.api'
})
此配置表示,所有請求中以 test 開頭的地址,都會交由 test.api 下的模塊進行處理。
使用裝飾器 route 標記路由處理函數。
test/api/demo.py
from django.http import HttpRequest
from restful_dj.decorator import route
@route(module='module-name', name='name')
def get(request, param1, param2=None, param3: int =5):
# request 會是 HttpRequest
return {
'param1': param1,
'param2': param2,
'param3': param3,
}
@route(module='module-name', name='name')
def get_param(param1, req: HttpRequest, from_=None, param3 =5):
# req 會是 HttpRequest
return {
'param1': param1,
'from': from_,
'param3': param3,
}
@route(module='module-name', name='name')
def get_param(request: str, param1, from_=None, param3 =5):
# request 會是請求參數,參數列表中沒有 HttpRequest
return {
'request': request,
'param1': param1,
'from': from_,
'param3': param3,
}
@route(module='module-name', name='name')
def get_param(request, param1, from_=None, param3 =5, **kwargs):
# 未在函數的參數列表中聲明的請求參數,會出現在 kwargs 中
return {
'param1': param1,
'from': from_,
'param3': param3,
'variable_args': kwargs
}
一些需要注意的地方:
當代碼中需要使用關鍵字作為名稱時,請在名稱后添加 _,此時前端請求時,_ 符號可省略,
如: from_ 在請求時可寫作 from=test (from_=test 亦可)。
對于語言間的命令差異,可以自動兼容 下劃線命名法 與 駝峰命名法,如:請求參數中的 pageIndex,在處理函數中可以寫作 page_index或_page_index_ ,
也就是說,在前后添加 _ 符號都不會影響參數的解析。
路由處理函數可以添加一個可變參數(如:**kwargs**),用于接收未在參數列表中列出的請求項。
當然,kwargs和普通函數一樣,可以是任何其它名稱。
request 參數(與參數位置無關),可能被解析成三種結果(1和2均會將其作為 HttpRequest 參數處理):
參數名稱為 request,并且未指定參數類型(或指定類型為 HttpRequest)
參數類型為 HttpRequest,參數名稱可以是任何合法的標識符
參數名稱為 request,聲明了不是 HttpRequest 的類型,此時會被解析成一般的請求參數
前端調用:
// 請求 get 函數
ajax.get('test.demo?param1=1¶m2=2¶m3=3')
// 請求 get_param 函數
ajax.get('test.demo/param?param1=1¶m2=2¶m3=3')
路由可以返回任何類型的數據。路由會自動根據函數定義來判斷傳入參數的類型是否合法。
比如前面示例中的 param3: int =5,會根據聲明類型 int 去判斷傳入類型
如果傳入了字符串類型的數值,路由會自動轉換成數值類型
另外,如果設置了 None 以外的默認值,那么路由會根據默認值的類型自動去判斷,
此時可以省略參數類型,如: param3: int =5 省略為 param3=5
裝飾器
裝飾器 route 用于聲明某個函數可以被路由使用。通過添加此裝飾器以限制非路由函數被非法訪問。
聲明為:
def route(module=None, name=None, **kwargs):
pass
module 此路由所屬的業務/功能模塊名稱
name 此路由的名稱
**kwargs 額外參數
這些參數都會被傳遞給中間件的各個函數的參數 meta。詳細見 RouteMeta
同時,此裝飾器會自動嘗試將 request.body,request.GET 和 request.POST
處理成 JSON 格式(僅在 content-type=application/json 時),
并且分別添加到 request.B,request.G 和 request.P 屬性上。
注意:一般情況下,使用路由處理函數就能完全操作請求參數,應該盡量減少使用 B/P/G,以避免代碼的不明確性。
發布
發布 指將 Django 項目發布到服務器上運行(線上環境)。
一般來說,發布時只需要調用 生成路由映射文件 的接口就可以了,
路由收集在其中會自動調用。
路由收集
路由收集器用于收集項目中的所有路由,通過以下方式調用:
import restful_dj
routes = restful_dj.collect()
routes 是一個可以直接迭代的路由數組
其每一個路由項的結構如下:
module
name
kwargs
id
pkg # 路由所在包名稱
file # 路由所在文件的完整路徑
handler # 路由請求的處理函數
method # 路由的請求方法
path # 路由的請求路徑
生成路由映射文件
since 1.0.3
restful-dj 導出了一個工具函數 persist,用于將路由收集起來,并持久化,其用法如下:
import os
from django.conf import settings
import restful_dj
restful_map = os.path.join(settings.BASE_DIR, 'path/to/restful_map.py')
# restful_map 參數是可選的,當不傳時,調用會返回生成的代碼內容
# encoding 參數是可選的,默認值為 utf-8。
restful_dj.persist(restful_map, encoding='utf-8')
此處還需要調用路由的映射注冊,以及全局類型注冊等。
因此,最佳方法就是,將這些注冊寫一個單獨的 python 文件,在啟動和發布時均調用即可。
最終生成的路由代碼會寫入文件 restful_map.py,此文件會暴露一個數據項 routes,其中是所有的路由映射。
一般來說,應該在系統啟動時 (在主應用的 urls.py 文件中) 調用此函數:
import restful_dj
from path.to import restful_map
restful_dj.register_routes(restful_map.routes)
綜上,發布以及線上運行流程為:
發布時調用 restful_dj.persist 生成路由映射文件
程序啟動時,判斷 settings.DEBUG=False,執行 from path.to import restful_map
并調用 restful_dj.register_routes(restful_map.routes) 注冊路由。
高級用法
分發前的處理
有的時候,需要在分發前對請求參數進行處理。此時可以使用 restful.set_before_dispatch_handler 來進行一些預處理。
函數簽名:
def set_before_dispatch_handler(handler):
pass
用法:
import restful_dj
def before_dispatch_handler(request, entry, name):
# 可以在此處修改 request 的數據
# 也可以重新定義 entry 和 name
return entry, name
restful_dj.set_before_dispatch_handler(before_dispatch_handler)
注冊全局類型
當在路由裝飾器參數中使用了自定義的值類型時(比如枚舉或類),應該當將其注冊到 restful-dj,否則無法正確收集到路由。
例:
test.py
from restful_dj import route
from enums import RouteTypes
@route('module_name', 'route_name', route_type = RouteTypes.TEST)
def test(req):
pass
enums.py
from enum import Enum
class RouteTypes(Enum):
TEST = 1
注冊自定義日志記錄器
from restful_dj import set_logger
def my_logger(level: str, message: str, e: Exception):
pass
set_logger(my_logger)
其中,level表示日志級別,會有以下值:
debug
info
success
warning
error
中間件類結構
path.to.MiddlewareClass
from django.http import HttpRequest
from restful_dj import RouteMeta
class MiddlewareClass:
"""
路由中間件
"""
def process_request(self, request: HttpRequest, meta: RouteMeta, **kwargs):
"""
對 request 對象進行預處理。一般用于請求的數據的解碼,此時路由組件尚水進行請求數據的解析(B,P,G 尚不可用)
:param request:
:param meta:
:return: 返回 HttpResponse 以終止請求,返回 False 以停止執行后續的中間件(表示訪問未授權),返回 None 或不返回任何值繼續執行后續中間件
"""
pass
def process_invoke(self, request: HttpRequest, meta: RouteMeta, **kwargs):
"""
在路由函數調用前,對其參數等進行處理,此時路由組件已經完成了請求數據的解析(B,P,G 已可用)
此時可以對解析后的參數進行變更
:param request:
:param meta:
:return: 返回 HttpResponse 以終止請求,返回 False 以停止執行后續的中間件(表示訪問未授權),返回 None 或不返回任何值繼續執行后續中間件
"""
pass
def process_return(self, request: HttpRequest, meta: RouteMeta, **kwargs):
"""
在路由函數調用后,對其返回值進行處理
:param request:
:param meta:
:param kwargs: 始終會有一個 'data' 的項,表示返回的原始數據
:return: 返回 HttpResponse 以終止執行,否則返回新的 return value
"""
assert 'data' in kwargs
return kwargs['data']
def process_response(self, request: HttpRequest, meta: RouteMeta, **kwargs):
"""
對 response 數據進行預處理。一般用于響應的數據的編碼
:rtype: HttpResponse
:param meta:
:param request:
:param kwargs: 始終會有一個 'response' 的項,表示返回的 HttpResponse
:return: 無論何種情況,應該始終返回一個 HttpResponse
"""
assert 'response' in kwargs
return kwargs['response']
RouteMeta
路由元數據,中間件中勾子函數的參數 meta 結構。
from types import FunctionType
from typing import OrderedDict
class RouteMeta:
@property
def handler(self) -> FunctionType:
"""
路由處理函數對象
:return:
"""
return self._handler
@property
def func_args(self) -> OrderedDict:
"""
路由處理函數參數列表
:return:
"""
return self._func_args
@property
def id(self) -> str:
"""
路由ID,此ID由路由相關信息組合而成
:return:
"""
return self._id
@property
def module(self) -> str:
"""
裝飾器上指定的 module 值
:return:
"""
return self._module
@property
def name(self) -> str:
"""
裝飾器上指定的 name 值
:return:
"""
return self._name
@property
def kwargs(self) -> dict:
"""
裝飾器上指定的其它參數
:return:
:rtype: Dict
"""
return self._kwargs
另外,meta 還提供了 has 和 get 兩個方法,其描述如下:
has(arg_name) 判斷是否指定了額外參數
get(arg_name, default_value=None) 若存在指定名稱的額外參數,則返回值,否則返回指定的默認值
額外參數: 除 name 和 module 外的參數
待辦事項
添加嚴格模式支持。在嚴格模式下,不允許傳入未聲明的參數。
參數類型支持上傳文件
常見問題
403 Forbidden. CSRF verification failed. Request aborted.
移除或注釋掉 settings.py 文件中的中間件 'django.middleware.csrf.CsrfViewMiddleware'
restful 請求時,前端頁面并沒有 Django 生成的 form 以及對應的 csrf 校驗字段,此配置實際上對 restful 是沒有意義的。
更新記錄
2.0.0
新的 restful-dj 啟動方式
修復 api 列表頁面可能導致的異常問題
移除 DotDict 支持
移除 @route 的保留參數
1.0.3
優化 線上路由加載方式,提升啟動和響應速度
優化 路由處理函數被用戶直接調用的支持
優化 將兼容性調整為 Python 3.5
總結
以上是生活随笔為你收集整理的python restful 框架_restful-dj的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 牙齿松动可以做烤瓷牙吗
- 下一篇: 一颗牙都没有了能装活动假牙嘛