53web開發(fā)5_路由分組-創(chuàng)新互聯(lián)

目錄

十多年的印江網(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對象;

VER1:

例:

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語句;

VER2:

例:

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á)式簡化:

目前路由匹配使用正則表達(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'>})

VER3:

目前處理流程:

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

53web開發(fā)5_路由分組

另外有需要云服務(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://muchs.cn/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)

外貿(mào)網(wǎng)站建設(shè)