restful-djrestful 自動(dòng)路由框架
restful-dj 是一個(gè)基于 Django2/3 的 restful 自動(dòng)路由框架。
此包解決的問題:
- 告別 Django 繁鎖的路由配置
- 更便捷的 restful 編碼體驗(yàn)
- 自動(dòng)解析/校驗(yàn)請(qǐng)求參數(shù),填充到路由處理函數(shù)
安裝
Gitee: https://gitee.com/hyjiacan/restful-dj Github: https://github.com/hyjiacan/restful-dj PyPI: https://pypi.org/project/restful-dj/
pip install restful-dj
使用
此組件提供的包(package)名稱為 restful_dj,所有用到的模塊都在此包下引入。
注冊(cè)
在項(xiàng)目的根 urls.py 文件中,使用以下配置
from django.urls import path
import restful_dj
urlpatterns = [
path('any/prefix', restful_dj.urls)
]
其中,any/prefix 是用戶自定義的url前綴,可以使用空串 ''
注:可以通過部署地址 any/prefix 訪問接口列表 (僅在開發(fā)模式時(shí)可用)。
配置
配置項(xiàng)需要寫到 settings.py 文件中。
RESTFUL_DJ = {
'global_class': ['path.to.CustomClass',],
'routes': {
'path.prefix': 'path.to',
},
'middleware': [
'path.to.MiddlewareClass'
],
'logger': 'path.to.logger'
}
- global_class 當(dāng)在路由裝飾器參數(shù)中使用了自定義的值類型時(shí)(比如枚舉或類),應(yīng)該當(dāng)將其添加到此處,否則無法正確收集到路由
- routes 路由映射配置,即將指定的請(qǐng)求路徑映射到指定的代碼路徑(路徑應(yīng)為基于項(xiàng)目根目錄的相對(duì)路徑)
- middleware 中間件配置,其值為一個(gè)
list,第一個(gè)填寫一個(gè)中間件的完整限定名稱 - logger 默認(rèn)的日志是直接打印到控制臺(tái)的,可以通過設(shè)置此值以實(shí)現(xiàn)重定向日志輸出
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
編寫路由
路由文件位置沒有要求,只要配置好就可以了。
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 會(huì)是 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 會(huì)是 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 會(huì)是請(qǐng)求參數(shù),參數(shù)列表中沒有 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): # 未在函數(shù)的參數(shù)列表中聲明的請(qǐng)求參數(shù),會(huì)出現(xiàn)在 kwargs 中 return { 'param1': param1, 'from': from_, 'param3': param3, 'variable_args': kwargs }
一些需要注意的地方:
- 當(dāng)代碼中需要使用關(guān)鍵字作為名稱時(shí),請(qǐng)?jiān)诿Q后添加
_,此時(shí)前端請(qǐng)求時(shí),_符號(hào)可省略, 如:from_在請(qǐng)求時(shí)可寫作from=test(from_=test亦可)。 - 對(duì)于語言間的命令差異,可以自動(dòng)兼容
下劃線命名法與駝峰命名法,如:請(qǐng)求參數(shù)中的pageIndex,在處理函數(shù)中可以寫作page_index或_page_index_, 也就是說,在前后添加_符號(hào)都不會(huì)影響參數(shù)的解析。 - 路由處理函數(shù)可以添加一個(gè)可變參數(shù)(如:
**kwargs**),用于接收未在參數(shù)列表中列出的請(qǐng)求項(xiàng)。 當(dāng)然,kwargs和普通函數(shù)一樣,可以是任何其它名稱。 -
request參數(shù)(與參數(shù)位置無關(guān)),可能被解析成三種結(jié)果(1和2均會(huì)將其作為HttpRequest參數(shù)處理):- 參數(shù)名稱為
request,并且未指定參數(shù)類型(或指定類型為HttpRequest) - 參數(shù)類型為
HttpRequest,參數(shù)名稱可以是任何合法的標(biāo)識(shí)符 - 參數(shù)名稱為
request,聲明了不是HttpRequest的類型,此時(shí)會(huì)被解析成一般的請(qǐng)求參數(shù)
- 參數(shù)名稱為
再寫配置
RESTFUL_DJ = {
'routes': {
'test': 'test.api',
}
}
此配置表示,所有請(qǐng)求中以 test 開頭的地址,都會(huì)交由 test.api 下的模塊進(jìn)行處理。
前端調(diào)用:
// 請(qǐng)求 get 函數(shù) ajax.get('test.demo?param1=1¶m2=2¶m3=3') // 請(qǐng)求 get_param 函數(shù) ajax.get('test.demo/param?param1=1¶m2=2¶m3=3')
路由可以返回任何類型的數(shù)據(jù)。路由會(huì)自動(dòng)根據(jù)函數(shù)定義來判斷傳入?yún)?shù)的類型是否合法。
比如前面示例中的 param3: int =5,會(huì)根據(jù)聲明類型 int 去判斷傳入類型
- 如果傳入了字符串類型的數(shù)值,路由會(huì)自動(dòng)轉(zhuǎn)換成數(shù)值類型
- 另外,如果設(shè)置了
None以外的默認(rèn)值,那么路由會(huì)根據(jù)默認(rèn)值的類型自動(dòng)去判斷, 此時(shí)可以省略參數(shù)類型,如:param3: int =5省略為param3=5
裝飾器 route
裝飾器 route 用于聲明某個(gè)函數(shù)可以被路由使用。通過添加此裝飾器以限制非路由函數(shù)被非法訪問。
聲明為:
def route(module=None, name=None, permission=True, ajax=True, referer=None, **kwargs): pass
-
module此路由所屬的業(yè)務(wù)/功能模塊名稱 -
name此路由的名稱 -
permission設(shè)置此路由是否有權(quán)限控制 -
ajax設(shè)置此路由是否僅允許 ajax 訪問 保留參數(shù) -
referer設(shè)置此路由允許的 referer 地址 保留參數(shù) -
**kwargs額外參數(shù)
這些參數(shù)都會(huì)被傳遞給中間件的各個(gè)函數(shù)的參數(shù)
meta。詳細(xì)見 RouteMeta
同時(shí),此裝飾器會(huì)自動(dòng)嘗試將 request.body 處理成 JSON 格式(僅在 content-type=application/json 時(shí)),并且添加到 request.B 屬性上。
另外,此裝飾器會(huì)自動(dòng)將 request.GET 和 request.POST 處理成為可以通過點(diǎn)號(hào)直接訪問的對(duì)象,分別添加到 request.G 和 request.P 上。例:
# 原始數(shù)據(jù) request = {...} request.GET = { 'param1': 1, 'param2': 2 } # 此時(shí),只能這樣訪問 request.GET['param1'] # 而在經(jīng)過裝飾器的處理后,可以這樣訪問 request.G.param1
但是,一般情況下,使用路由處理函數(shù)就能完全操作請(qǐng)求參數(shù)的, 所以需要在代碼中盡量減少使用 B/P/G,以避免代碼的不明確性。
分發(fā)前的處理 (可選)
有的時(shí)候,需要在分發(fā)前對(duì)請(qǐng)求參數(shù)進(jìn)行處理。此時(shí)可以使用 restful.set_before_dispatch_handler 來進(jìn)行一些預(yù)處理。
函數(shù)簽名:
def set_before_dispatch_handler(handler):
pass
用法:
import restful_dj
def before_dispatch_handler(request, entry, name):
# 可以在此處修改 request 的數(shù)據(jù)
# 也可以重新定義 entry 和 name
return entry, name
restful_dj.set_before_dispatch_handler(before_dispatch_handler)
編寫中間件 (可選)
RouteMeta
路由元數(shù)據(jù)。
from types import FunctionType
from typing import OrderedDict
class RouteMeta:
@property
def handler(self) -> FunctionType:
"""
路由處理函數(shù)對(duì)象
:return:
"""
return self._handler
@property
def func_args(self) -> OrderedDict:
"""
路由處理函數(shù)參數(shù)列表
:return:
"""
return self._func_args
@property
def id(self) -> str:
"""
路由ID,此ID由路由相關(guān)信息組合而成
: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 permission(self) -> bool:
"""
裝飾器上指定的 permission 值
:return:
"""
return self._permission
@property
def ajax(self) -> bool:
"""
裝飾器上指定的 ajax 值
:return:
"""
return self._ajax
@property
def referer(self) -> str:
"""
裝飾器上指定的 referer 值
:return:
"""
return self._referer
@property
def kwargs(self) -> dict:
"""
裝飾器上指定的其它參數(shù)
:return:
:rtype: Dict
"""
return self._kwargs
注冊(cè)到 settings.py 的 RESTFUL_DJ.middleware 列表中。中間件將按順序執(zhí)行。
需要注意: 每一個(gè)中間件在程序運(yùn)行期間共享一個(gè)實(shí)例。
path.to.MiddlewareClass
from django.http import HttpRequest
from restful_dj import RouteMeta
class MiddlewareClass:
"""
路由中間件
"""
def process_request(self, request: HttpRequest, meta: RouteMeta, **kwargs):
"""
對(duì) request 對(duì)象進(jìn)行預(yù)處理。一般用于請(qǐng)求的數(shù)據(jù)的解碼,此時(shí)路由組件尚水進(jìn)行請(qǐng)求數(shù)據(jù)的解析(B,P,G 尚不可用)
:param request:
:param meta:
:return: 返回 HttpResponse 以終止請(qǐng)求,返回 False 以停止執(zhí)行后續(xù)的中間件(表示訪問未授權(quán)),返回 None 或不返回任何值繼續(xù)執(zhí)行后續(xù)中間件
"""
pass
def process_invoke(self, request: HttpRequest, meta: RouteMeta, **kwargs):
"""
在路由函數(shù)調(diào)用前,對(duì)其參數(shù)等進(jìn)行處理,此時(shí)路由組件已經(jīng)完成了請(qǐng)求數(shù)據(jù)的解析(B,P,G 已可用)
此時(shí)可以對(duì)解析后的參數(shù)進(jìn)行變更
:param request:
:param meta:
:return: 返回 HttpResponse 以終止請(qǐng)求,返回 False 以停止執(zhí)行后續(xù)的中間件(表示訪問未授權(quán)),返回 None 或不返回任何值繼續(xù)執(zhí)行后續(xù)中間件
"""
pass
def process_return(self, request: HttpRequest, meta: RouteMeta, **kwargs):
"""
在路由函數(shù)調(diào)用后,對(duì)其返回值進(jìn)行處理
:param request:
:param meta:
:param kwargs: 始終會(huì)有一個(gè) 'data' 的項(xiàng),表示返回的原始數(shù)據(jù)
:return: 返回 HttpResponse 以終止執(zhí)行,否則返回新的 return value
"""
assert 'data' in kwargs
return kwargs['data']
def process_response(self, request: HttpRequest, meta: RouteMeta, **kwargs):
"""
對(duì) response 數(shù)據(jù)進(jìn)行預(yù)處理。一般用于響應(yīng)的數(shù)據(jù)的編碼
:rtype: HttpResponse
:param meta:
:param request:
:param kwargs: 始終會(huì)有一個(gè) 'response' 的項(xiàng),表示返回的 HttpResponse
:return: 無論何種情況,應(yīng)該始終返回一個(gè) HttpResponse
"""
assert 'response' in kwargs
return kwargs['response']
設(shè)置日志記錄器 (可選)
from restful_dj import set_logger def my_logger(level: str, message: str, e: Exception): pass set_logger(my_logger)
其中,level表示日志級(jí)別,會(huì)有以下值:
- debug
- info
- success
- warning
- error
路由收集
路由收集器用于收集項(xiàng)目中的所有路由,通過以下方式調(diào)用:
from restful_dj import collector routes = collector.collect()
routes是一個(gè)可以直接迭代的路由數(shù)組
在發(fā)布產(chǎn)品到線上時(shí),通過此方法將自動(dòng)產(chǎn)生 Django 的路由配置文件,以提高線上性能。
