徒手撸出一个类Flask微框架根据业务进行路由分组
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了徒手撸出一个类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微框架根据业务进行路由分组的主要内容,如果未能解决你的问题,请参考以下文章