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

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

如:app

/product/(\w+)/(?P<id>\d+        # 匹配/product/123123  的前綴

好比什麼類別,類別下的什麼產品 等,
ide

用request path進行正則匹配,因此須要用到正則分組post

分析咱們當前代碼,只有__call__方法纔是真正在作處理,也就是說得在這個方法獲取分組url

經過動態屬性spa

經過動態屬性,將匹配到的命名分組域當前request對象,由於一個請求對應不一樣的request實例code

這裏是一個實例只能對應一個requestorm

@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')

對咱們而言命名後的分組纔有價值,動態增長屬性以後,增長了屬性,爲了獲取分組信息
router

打印這兩個以下:server

>>>>>> /
>>>>>> {

每個前綴所對應的字段咱們認爲是不一樣的路由實例,每一個實例保存本身的前綴,在內部實現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()
相關文章
相關標籤/搜索