徒手擼出一個類Flask微框架(三)根據業務進行路由分組
如:
/product/(\w+)/(?P<id>\d+ # 匹配/product/123123 的前綴
比如什麽類別,類別下的什麽產品 等,
用request path進行正則匹配,所以需要用到正則分組
分析我們當前代碼,只有__call__方法才是真正在做處理,也就是說得在這個方法獲取分組
通過動態屬性
通過動態屬性,將匹配到的命名分組域當前request對象,因為一個請求對應不同的request實例
這裏是一個實例只能對應一個request
@dec.wsgify def __call__(self, request:Request): for methods, pattern, handler in self.ROUTE: print(self.ROUTE) if not methods or request.method in methods: if request.method.upper() in methods: matcher = pattern.match(request.path) if matcher: request.args = matcher.group() #所有的分組 request.kwargs = matcher.groupdict() #所有的命名分組 return handler(request) raise exc.HTTPNotFound('no')
對我們而言命名後的分組才有價值,動態增加屬性之後,增加了屬性,為了獲取分組信息
打印這兩個如下:
>>>>>> / >>>>>> {
每一個前綴所對應的字段我們認為是不同的路由實例,每個實例保存自己的前綴,在內部實現Application,這一就將Application變為多個實例
思路:通過request的前綴名來判斷不同的業務,通過不同的前綴名來調用不同的方法
比上一節多了一級
需求:
URL 為/product/12345,那麽需要將產品id 12345取出,用分組包裝,一旦分離出來前綴product,那麽這就是一個一級路由分組,通過第一級判斷交給誰來做
再通過Applicaction 判斷前綴扔給不同的對象,而對象內又對應不同的pattern(正則)再交給不同的handler
例:
p = Router('/product') 直接轉化前綴
p.get(pattern正則) 匹配路徑,replice之後換為空,切分後再判斷
提交的模式
/product/(\w+)/(?P<id>\d+)
可以理解為每一個前綴都是一個不同的路由實例,每個實例保留的是業務分組
前綴必須以/根開始
前綴不能在後面加/ ,用戶不認這個,只能strip掉
建立隸屬關系
一個prefix下有若幹個url,建立了某種關系,比如/xxx/xx 這裏URL屬於xxx的管理範圍;只要符合格式就屬於prefix的管轄範圍
一個Router類 通過每一個類都管理一個prefix
之前所有方法都是在Application類方法中,現在要將其拆開
現在路由表方式應該如何處理?
不同前綴對應不同的router實例,那麽這些方法就不是類方法了,而是對應一個實例方法
我們現在想讓每個業務分開,各管各的,獨立負責自己的路由信息對應不同的實例;
如果是一個實例的話那麽可以獨立管理,那麽如果是類屬性則不適用,因為類屬性是共享的,所以要由實例自己管理獨立的路由表
定義Application類,只保存所有註冊的路由對象,__call__依然是回調的入口,一切從這裏開始
在這裏需要遍歷所有的Rtouer實例,這裏是Router實例不是類屬性
找到實例之後返回response即可
路由分組
按照前綴分別映射
分析:
application是作為入口的東西是單一的,所以需要固定
大多數server 需要傳遞一個單一的對象,會找到入口wsgi的入口,也就是__call__
首先知道每一個前綴的管理是不同的對象,這個類為Router
將註冊的方法移到Router類,實現一個實例管理當前業務的url,實際上還是meatch方法
原則:只找一個route
代碼如下:
解決前綴問題
# /product/tv/1234 /product/tv/abc # /python/student/16 # /product/(\w+)/(?<id>\d+)
找到定義初始化,一個實例獨立管理,路由要在初始化的時候定義好
prefix 是前綴,默認為空;
每個實例自行管理自己的路由表;
class Router: def __init__(self,prefix:str=''): self.__prefix = prefix self.__routetable = []
定義route
@property def prefix(self): return self.__routetable def route(self, pattern, *methods): # 傳入正則和路徑 def wapper(handler): uri = (methods,re.compile(pattern),handler) self.__routetable.append(uri) return handler return wapper
定義請求方法
@property def prefix(self): return self.__prefix def get(self,pattern): return self.route('GET', pattern) def post(self,pattern): return self.route('POST', pattern)
定義maetch **
·判斷是否屬於自己實例管理的prefix
·如果不是以它為前綴的直接return None
·如果歸屬自己管理所有循環作完沒有匹配到直接默認return None 或者404
·並修改前綴 通過replace,因為傳遞url是帶的,但是在處理的時候是
def match(self,request:Request): # 如果不是以某為前綴則直接return空,那麽則沒有註冊,當__call__掃描的時候沒有獲取到則直接404 if not request.path.startswith(self.prefix): return for methods, pattern, handler in self.__routetable: if not methods or request.method.upper() in methods: # 我們寫的pattern是/produ ct/(\w+)/(?<id>\d+),匹配的是後半部分,那匹配也是後半部分,所以要去掉前綴 matcher = pattern.match(request.path.replace(self.prefix)) if matcher: request.kwargs = matcher.groupdict() return handler
定義Application
定義Application
·定義ROUTERS列表用於管理實例對象
·如果是外界可以註冊進來,那肯定不是實例,所以還需要類方法
註冊的方式:
p = Router('/product') 直接轉化前綴
p.get(pattern正則) 匹配路徑,replice之後換為空,切分後再判斷
沒有必要實例化,註冊的東西都需要在這之上
class Application: ROUTERS = [] #將Router註冊進來 @classmethod def register(cls,router:Router): cls.ROUTERS.append(router)
定義__call__方法
查找每個ROUTERS 返回response,前提是找到request之後
如果能處理則必須返回數據,判斷是否有數據要麽None要麽非None
@dec.wsgify def __call__(self, request:Request): # 獲取response,問每一個router進行詢問,調用實例化的router進行match方法,將request傳遞過去 for router in self.ROUTERS: response = router.match(request) # 一旦有數據獲取那麽直接返回response,如果全部執行完沒有路由,則直接404 if response: return response raise exc.HTTPNotFound('no')
如果沒有思路的話先定義application最後再定義match方法
註冊
idx = Router() py = Router('/py')
這樣一寫,肯定是由註冊方法進行註冊,所以還需要調用註冊方法
Application.register(idx) Application.register(py)
只要application能夠調用,那麽就直接到__call__中遍歷
這樣通過業務的分級進行分別管理
完整如下:
class Router: def __init__(self,prefix:str=''): # /product/tv/1234 /product/tv/abc # /python/student/16 # /produ ct/(\w+)/(?<id>\d+) self.__prefix = prefix self.__routetable = [] def route(self,pattern,*methods): def wapper(handler): uri = (methods,re.compile(pattern),handler) print('uri:',uri) self.__routetable.append(uri) return handler return wapper def get(self,pattern): return self.route(pattern,'GET') def post(self,pattern): return self.route(pattern,'POST') def match(self,request:Request): if not request.path.startswith(self.__prefix): return None for methods,pattern,handler in self.__routetable: if not methods or request.method.upper() in methods: matcher = pattern.match(request.path.replace(self.__prefix,'',1)) if matcher: request.kwargs = matcher.groupdict() return handler(request) class App: ROUTERS = [] @classmethod def register(cls,router:Router): cls.ROUTERS.append(router) @dec.wsgify def __call__(self, request:Request): for router in self.ROUTERS: response = router.match(request) return response raise exc.HTTPNotFound('no') idx = Router() App.register(idx) @idx.get('^/$') def index(request:Request): res = Response() res.body = 'aa'.encode() return res if __name__ == "__main__": ip = '127.0.0.1' port = 9999 server = make_server(ip,port,App()) server.serve_forever() server.server_close()
徒手擼出一個類Flask微框架(三)根據業務進行路由分組