目錄
十多年的印江網(wǎng)站建設(shè)經(jīng)驗,針對設(shè)計、前端、開發(fā)、售后、文案、推廣等六對一服務(wù),響應(yīng)快,48小時及時工作處理。全網(wǎng)營銷推廣的優(yōu)勢是能夠根據(jù)用戶設(shè)備顯示端的尺寸不同,自動調(diào)整印江建站的顯示方式,使網(wǎng)站能夠適用不同顯示終端,在瀏覽器中調(diào)整網(wǎng)站的寬度,無論在任何一種瀏覽器上瀏覽網(wǎng)站,都能展現(xiàn)優(yōu)雅布局與設(shè)計,從而大程度地提升瀏覽體驗。成都創(chuàng)新互聯(lián)公司從事“印江網(wǎng)站設(shè)計”,“印江網(wǎng)站推廣”以來,每個客戶項目都認(rèn)真落實執(zhí)行。路由分組:...1
VER1:...2
字典訪問屬性化:...4
VER2:...6
正則表達(dá)式簡化:...7
分組捕獲:
支持正則表達(dá)式的捕獲;
什么時候捕獲?在框架回調(diào)__call__()時,送入request,拿到request.path和正則的模式匹配后,就可提取分組;
如何處理分組?web app就是handler對應(yīng)的不同的函數(shù),其參數(shù)request是一樣的,將捕獲的數(shù)據(jù)動態(tài)的增加到request對象上;
用動態(tài)增加屬性,為request增加args、kwargs屬性,在handler中使用時,就可直接從屬性中將args、kwargs拿出來直接用;
request.args=matcher.group()?? #所有分組組成的元組,包括命名的
request.kwargs=matcher.groupdict()?? #命名分組組成的字典,用此種
所謂路由分組,就是按前綴分別映射;
如下,是不同業(yè)務(wù):
/product/tv/1234
/python/student/3456
/product/(\w+)/(?P<id>\d+)
/product和/python為一級目錄,常用的/admin(后臺管理),可稱為prefix,前綴必須以/開頭,不能以分隔符結(jié)尾;
如何建立prefix和url之間的隸屬關(guān)系?一個prefix下可有若干個url,這些url都屬于這個prefix的;
建立一個Router類,里面保存prefix,同時保存url和handler的對應(yīng)關(guān)系;
之前,所有注冊方法都是Application的類方法,即所有映射都保存在ROUTER_TABLE類屬性中,但現(xiàn)在不同前綴就是不同的Router實例,所有注冊方法,都成了實例的方法,路由表由Router實例自己管理;
Application中當(dāng)前只需要保存所有注冊的Router對象(實例)就行,__call__()依然是回調(diào)入口,在其中遍歷所有Router實例,找到路徑匹配的Router實例,讓Router實例返回Response對象;
例:
from wsgiref.simple_server import make_server
from webob import Request, Response, dec, exc
import re
class Router:
def __init__(self, prefix: str=''):
self.__prefix = prefix.rstrip('/\\')?? #/python/或/python\\轉(zhuǎn)為/python,注意不能用strip()
self.__routertable = []?? #[(methods, re.compile(pattern), handler)]
@property
def prefix(self):?? #為之后使用方便,設(shè)為類屬性方式
return self.__prefix
def route(self, pattern, *methods):
def wrapper(handler):
self.__routertable.append((methods, re.compile(pattern), handler))
return handler
return wrapper
def get(self, pattern):
return self.route(pattern, 'GET')
def post(self, pattern):
return self.route(pattern, 'POST')
def head(self, pattern):
return self.route(pattern, 'HEAD')
def match(self,request:Request)->Response:
if not request.path.startswith(self.prefix):?? #前綴處理,不是對應(yīng)的前綴直接返回None;字符串方法startswith()返回bool,startswith([prefix[,start[,end]])-->bool,prefix開頭
return
for methods, regex, handler in self.__routertable:
print(methods, regex, handler)
if not methods or request.method.upper() in methods:?? #not methods即支持全部方法
matcher = regex.search(request.path.replace(self.prefix, '', 1))?? #request.path路徑一定是prefix開頭,去掉prefix,剩下的才是正則匹配的路徑,replace(old,new[,count])-->new str
if matcher:
print(matcher)
request.kwargs = matcher.groupdict()?? #命名分組組成的字典
return handler(request)
# return?? #匹配不上返回None
class Application:
ROUTERS = []
@classmethod
def register(cls, router:Router):
return cls.ROUTERS.append(router)
@dec.wsgify
def __call__(self, request:Request) -> Response:
for router in self.ROUTERS:?? #遍歷ROUTERS,調(diào)用Router實例的match()方法看誰匹配
response = router.match(request)
if response:?? #匹配返回非None的Router對象,匹配則立即返回
return response
raise exc.HTTPNotFound('<h2>the page not found</h2>')
idx = Router()
py = Router('/python')
Application.register(idx)?? #一定要注冊
Application.register(py)
@py.get('/(\w+)')?? #匹配/python/xxxx,只支持get方法
def showpython(request):
res = Response()
res.body = '<h2>hello python</h2>'.encode()
return res
@idx.route('^/$')?? #只匹配根,支持所有方法
def index(request):
res = Response()
res.body = '<h2>welcome</h2>'.encode()
return res
if __name__ == '__main__':
ip = '127.0.0.1'
port = 9999
server = make_server(ip, port, Application())
try:
server.serve_forever()
except Exception as e:
print(e)
finally:
server.shutdown()
server.server_close()
d = { 'a': 8}
改造成這樣用:
d.a
d.a=9
例:
class DictObj:
def __init__(self, d: dict):
# self._dict = d?? #只要有屬性賦值都要調(diào)用__setattr__()方法,如有__setattr__()調(diào)用該方法并動態(tài)寫入到__dict__中,而該沒實現(xiàn)寫入
if isinstance(d, dict):?? #通常用if not isinstance(d, dict)
self.__dict__['_dict'] = d
else:
self.__dict__['_dict'] = {}
def __getattr__(self, item):
try:
# print(self._dict)
return self._dict[item]?? #不能用return getattr(self._dict, item)這種方式??????? ???????????????? except KeyError:
raise AttributeError('Attribute {} not found'.format(item))
def __setattr__(self, key, value):?? #不寫該方法則可添加屬性,與要求不符
# self._dict[key] = value
# print(key, value)
raise NotImplementedError
d = {'a':8}
do = DictObj(d)
print(do.__dict__)
print(do.a)?? #do.a和d['a']類似DB中的視圖和原本數(shù)據(jù)
# do.a=9?? #在注釋掉__setattr__()后,可添加屬性進(jìn)去
# print(do.a)
print(do.b)
輸出:
{'_dict': {'a': 8}}
8
Traceback (most recent call last):
File "E:/git_practice/cmdb/example_DictObj.py", line 13, in __getattr__
return self._dict[item]
KeyError: 'b'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "E:/git_practice/cmdb/example_DictObj.py", line 28, in <module>
print(do.b)
File "E:/git_practice/cmdb/example_DictObj.py", line 15, in __getattr__
raise AttributeError('Attribute {} not found'.format(item))
AttributeError: Attribute b not found
例,錯誤示例:
遞歸:
訪問實例屬性,先找__dict__再找__getattr__,兩處都沒有遞歸一直找;
凡是屬性訪問最后都找__getattr__;
class DictObj:
def __init__(self, d: dict):
self._dict = d
# if isinstance(d, dict):
#???? self.__dict__['_dict'] = d
# else:
#???? self.__dict__['_dict'] = {}
def __getattr__(self, item):
try:
# print(self._dict)
return self._dict[item]
except KeyError:
raise AttributeError('Attribute {} not found'.format(item))
def __setattr__(self, key, value):
self._dict[key] = value
# print(key, value)
# raise NotImplementedError
d = {'a':8}
do = DictObj(d)
print(do.__dict__)
print(do.a)
輸出:
……
File "E:/git_practice/cmdb/example_DictObj.py", line 13, in __getattr__
return self._dict[item]
RecursionError: maximum recursion depth exceeded
pycharm中調(diào)試程序:
先下斷點;
右鍵Debug "example_DictObj";
看棧,看變量;
斷點+print語句;
例:
class DictObj:
def __init__(self, d: dict):
if not isinstance(d, dict):
self.__dict__['_dict'] = {}
else:
self.__dict__['_dict'] = d
def __getattr__(self, item):
try:
return getattr(self._dict, item)
except KeyError:
raise AttributeError('Attribute {} Not Found '.format(self._dict))
def __setattr__(self, key, value):
raise NotImplementedError
class Router:
def __init__(self, prefix: str=''):
self.__prefix = prefix.rstrip('/\\')
self.__routertable = []
@property
def prefix(self):
return self.__prefix
def route(self, pattern, *methods):
def wrapper(handler):
self.__routertable.append((methods, re.compile(pattern), handler))
return handler
return wrapper
def get(self, pattern):
return self.route(pattern, 'GET')
def post(self, pattern):
return self.route(pattern, 'POST')
def head(self, pattern):
return self.route(pattern, 'HEAD')
def match(self,request:Request)->Response:
if not request.path.startswith(self.prefix):
return
for methods, regex, handler in self.__routertable:
???????print(methods, regex, handler)
if not methods or request.method.upper() in methods:
matcher = regex.search(request.path.replace(self.prefix, '', 1))
if matcher:
print(matcher)
???????????request.kwargs = DictObj(matcher.groupdict())
return handler(request)
# return
目前路由匹配使用正則表達(dá)式定義,不友好,很多用戶不會使用正則,能否簡化?
生產(chǎn)中,url是規(guī)范的,不能隨便寫,路徑是有意義的,尤其是restful風(fēng)格,所以要對url規(guī)范,如/product/123456,第1段是業(yè)務(wù),第2段是ID;
設(shè)計:
/student/{name:str}/{id:int}
類型設(shè)計,支持str、word、int、float、any類型;通過這樣的設(shè)計讓用戶簡化,同時也規(guī)范,背后的轉(zhuǎn)換編程者實現(xiàn);
另raw類型,直接支持RE;
str | 不包含/的任意字符 | [^/]+ |
word | 字母和數(shù)字 | \w+ |
int | 純數(shù)字,正負(fù)數(shù) | [+-]?\d+ |
float | 正負(fù)號、數(shù)字、包含. | [+-]?\d+.\d+ |
any | 包含/的任意字符 | .+ |
例:
import re
pattern = '/({[^{}:]+:?[^{}:]*})'
regex = re.compile(pattern)
s = '/student/{name:str}/xxx/{id:int}'
s1 = '/student/xxx/{id:int}/yyy'
s2 = '/student/{name:}/xxx/{id}'
s3 = '/student/xxx/133456'
s4 = '/student/{name:}/xxx/{id:aaa}'
# /{id:int} => /(?P<id>[+-]?\d+)
# '/(?<{}>{})'.format('id', TYPEPATTERNS['int'])
TYPEPATTERNS = {
'str': r'[^/]+',
'word': r'\w+',
'int': r'[+-]?\d+',
'float': r'[+-]?\d+.\d+',
'any': r'.+'
}
TYPECAST = {
'str': str,
'word': str,
'int': int,
'float': float,
'any': str
}
def transform(kv: str):
name, _, type = kv.strip('/{}').partition(':')?? #/{id:int}=>/(?P<id>[+-]\d+)
# name, type = kv.strip('/{}').split(':')?? #'/{id}'.strip('/{}').split(':'),split后返回['id']一個元素,type會拿不到值,報ValueError: not enough values to unpack (expected 2, got 1),所以此處只能用partition不能用split,partition始終返回3個元素
return '/(?P<{}>{})'.format(name, TYPEPATTERNS.get(type, '\w+')), name, TYPECAST.get(type, str)
def parse(src: str):
start = 0
res = ''
translator = {}
while True:
matcher = regex.search(src, start)
if matcher:
res += matcher.string[start:matcher.start()]
tmp = transform(matcher.string[matcher.start():matcher.end()])
res += tmp[0]
translator[tmp[1]] = tmp[2]
start = matcher.end()
else:
break
if res:
return res, translator
else:
return src, translator
print(parse(s))
print(parse(s1))
print(parse(s2))
print(parse(s3))
print(parse(s4))
輸出:
('/student/(?P<name>[^/]+)/xxx/(?P<id>[+-]\\d+)', {'id': <class 'int'>, 'name': <class 'str'>})
('/student/xxx/(?P<id>[+-]\\d+)', {'id': <class 'int'>})
('/student/(?P<name>\\w+)/xxx/(?P<id>\\w+)', {'id': <class 'str'>, 'name': <class 'str'>})
('/student/xxx/133456', {})
('/student/(?P<name>\\w+)/xxx/(?P<id>\\w+)', {'id': <class 'str'>, 'name': <class 'str'>})
目前處理流程:
b發(fā)來請求,被wsgi server調(diào)度給Application的__call__();
Application中遍歷注冊的Router,Router的match()方法來判斷是不是自己處理,先前綴再注冊的規(guī)則(規(guī)則被裝飾器已轉(zhuǎn)換成了命名分組的RE了);
若某個注冊的RE匹配,就把獲取到的參數(shù)放到request中,并調(diào)用注冊時映射的handler給它傳入request;
handler處理后,返回response,Application中拿到這個response數(shù)據(jù),返回給原始的wsgi server;
例:
from wsgiref.simple_server import make_server
from webob import Request, Response, dec, exc
import re
class DictObj:
def __init__(self, d: dict):
if not isinstance(d, dict):
self.__dict__['_dict'] = {}
else:
self.__dict__['_dict'] = d
def __getattr__(self, item):
try:
?????return self._dict[item]
except KeyError:
raise AttributeError('Attribute {} Not Found '.format(self._dict))
def __setattr__(self, key, value):
raise NotImplementedError
class Router:
pattern = '/({[^{}:]+:?[^{}:]*})'? # /{name:str}
regex = re.compile(pattern)
TYPEPATTERNS = {
'str': r'[^/]+',
'word': r'\w+',
'int': r'[+-]?\d+',
'float': r'[+-]\d+.\d+',
'any': r'.+'
}
TYPECAST = {
'str': str,
'word': str,
'int': int,
'float': float,
'any': str
}
def _transform(self, kv: str):
name, _, type = kv.strip('/{}').partition(':')
return '/(?P<{}>{})'.format(name, self.TYPEPATTERNS.get(type, '\w+')), name, self.TYPECAST.get(type, str)
def _parse(self, src: str):
start = 0
res = ''
translator = {}
while True:
matcher = self.regex.search(src, start)
if matcher:
res += matcher.string[start: matcher.start()]
tmp = self._transform(matcher.string[matcher.start():matcher.end()])
res += tmp[0]
translator[tmp[1]] = tmp[2]
start = matcher.end()
else:
break
if res:
return res, translator
else:
return src, translator
def __init__(self, prefix: str=''):
self.__prefix = prefix.rstrip('/\\')
self.__routertable = []?? #[(methods, regex, translator, handler)]
@property
def prefix(self):
return self.__prefix
def route(self, rule, *methods):
def wrapper(handler):
pattern, translator = self._parse(rule)
self.__routertable.append((methods, re.compile(pattern), translator, handler))
return handler
return wrapper
def get(self, pattern):
return self.route(pattern, 'GET')
def post(self, pattern):
return self.route(pattern, 'POST')
def head(self, pattern):
return self.route(pattern, 'HEAD')
def match(self, request: Request)->Response:
print(request.path)
if not request.path.startswith(self.prefix):
???return
for methods, regex, translator, handler in self.__routertable:
print(methods, regex, translator, handler)
if not methods or request.method.upper() in methods:
matcher = regex.search(request.path.replace(self.prefix, '', 1))
if matcher:
print(matcher)
newdict = {}
for k, v in matcher.groupdict().items():
newdict[k] = translator[k](v)
?print(newdict)
request.vars = DictObj(newdict)
return handler(request)
# return
class Application:
ROUTERS = []
@classmethod
def register(cls, router: Router):
return cls.ROUTERS.append(router)
@dec.wsgify
def __call__(self, request: Request) -> Response:
for router in self.ROUTERS:
response = router.match(request)
if response:
return response
???raise exc.HTTPNotFound('<h2>the page not found</h2>')
idx = Router()
py = Router('/python')
Application.register(idx)
Application.register(py)
# @py.get('/{name:str}')
# @py.get('/{id:int}')
@py.get('/{name:str}/{id:int}')
def showpython(request):
res = Response()
# print(request.__dict__)
# res.body = '<h2>hello python; vars = {}</h2>'.format(request.vars.name).encode()
res.body = '<h2>hello python; vars = {}</h2>'.format(request.vars.id).encode()
return res
@idx.route('^/$')
def index(request):
res = Response()
res.body = '<h2>welcome</h2>'.encode()
return res
if __name__ == '__main__':
ip = '127.0.0.1'
port = 9999
server = make_server(ip, port, Application())
try:
server.serve_forever()
except Exception as e:
print(e)
finally:
server.shutdown()
server.server_close()
輸出:
/python/test/456
() re.compile('^/$') {} <function index at 0x00000000033B1BF8>
/python/test/456
('GET',) re.compile('/(?P<name>[^/]+)/(?P<id>[+-]?\\d+)') {'name': <class 'str'>, 'id': <class 'int'>} <function showpython at 0x00000000033B1B70>
<_sre.SRE_Match object; span=(0, 9), match='/test/456'>
{'name': 'test', 'id': 456}
/favicon.ico
() re.compile('^/$') {} <function index at 0x00000000033B1BF8>
/favicon.ico
另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)cdcxhl.cn,海內(nèi)外云服務(wù)器15元起步,三天無理由+7*72小時售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國服務(wù)器、虛擬主機、免備案服務(wù)器”等云主機租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡單易用、服務(wù)可用性高、性價比高”等特點與優(yōu)勢,專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場景需求。
分享名稱:53web開發(fā)5_路由分組-創(chuàng)新互聯(lián)
網(wǎng)址分享:http://jinyejixie.com/article38/dsgosp.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站制作、網(wǎng)站營銷、小程序開發(fā)、定制網(wǎng)站、用戶體驗、網(wǎng)站設(shè)計
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容