1. 程式人生 > >徒手擼出一個類Flask微框架(三)根據業務進行路由分組

徒手擼出一個類Flask微框架(三)根據業務進行路由分組

轉化 但是 根據 ask ice bsp rgb 註冊方法 127.0.0.1

所謂分組就是按照前綴分布映射

如:

/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微框架(三)根據業務進行路由分組