徒手撸出一个类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()

原文地址:http://blog.51cto.com/yijiu/2071939

时间: 2024-10-10 10:16:10

徒手撸出一个类Flask微框架(三)根据业务进行路由分组的相关文章

徒手撸出一个类Flask微框架(二)路由及路由注册的演变

路由的基本概念: 根据不同的访问路径调用不同的方法或者类 from webob import Response,Request,dec from wsgiref.simple_server import make_server,demo_app def index(request:Request):     res = Response()     res.body = 'index.html'.encode()     return res def showpython(request:Req

徒手撸出一个类Flask微框架(一) 理解HTTP请求 request和response

environ 环境参数 考虑到后期的事宜,引入第三方库 webob https://docs.pylonsproject.org/projects/webob/en/stable/index.html 一.webob.request对象 将环境参数解析并封装成request对象 使用方法: GET 发送的数据是URL的查询字符串,在request头部信息中 就是一个字典MultiDict,封装着查询字符串 POST 提交,数据放在请求的body中,但是也可以同时使用QERY_STRING 只需

【PHP面向对象(OOP)编程入门教程】4.如何抽象出一个类?

上面已经介绍过了, 面向对象程序的单位就是对象,但对象又是通过类的实例化出来的,所以我们首先要做的就是如何来声明类, 做出来一个类很容易,只要掌握基本的程序语法定义规则就可以做的出来,那么难点在那里呢? 一个项目要用到多少个类,用多少个对象, 在那要定义类,定义一个什么样的类,这个类实例化出多少个对象, 类里面有多少个属性, 有多少个方法等等,这就需要读者通过在实际的开发中就实际问题分析设计和总结了. 类的定义: 1 2 class 类名 { } 使用一个关键字class和后面加上一个你想要的类

php--opp--4.如何抽象出一个类?

上面已经介绍过了, 面向对象程序的单位就是对象,但对象又是通过类的实例化出来的,所以我们首先要做的就是如何来声明类, 做出来一个类很容易,只要掌握基本的程序语法定义规则就可以做的出来,那么难点在那里呢? 一个项目要用到多少个类,用多少个对象, 在那要定义类,定义一个什么样的类,这个类实例化出多少个对象, 类里面有多少个属性, 有多少个方法等等,这就需要读者通过在实际的开发中就实际问题分析设计和总结了. 类的定义: class 类名 { } 使用一个关键字class和后面加上一个你想要的类名以及加

Golang 微框架 Gin 简介

框架一直是敏捷开发中的利器,能让开发者很快的上手并做出应用,甚至有的时候,脱离了框架,一些开发者都不会写程序了.成长总不会一蹴而就,从写出程序获取成就感,再到精通框架,快速构造应用,当这些方面都得心应手的时候,可以尝试改造一些框架,或是自己创造一个. 曾经我以为Python世界里的框架已经够多了,后来发现相比golang简直小巫见大巫.golang提供的net/http库已经很好了,对于http的协议的实现非常好,基于此再造框架,也不会是难事,因此生态中出现了很多框架.既然构造框架的门槛变低了,

徒手撸框架--实现 RPC 远程调用

微服务,已经是每个互联网开发者必须掌握的一项技术.而 RPC 框架,是构成微服务最重要的组成部分之一.趁最近有时间.又看了看 dubbo 的源码.dubbo 为了做到灵活和解耦,使用了大量的设计模式和 SPI机制,要看懂 dubbo 的代码也不太容易. 按照<徒手撸框架>系列文章的套路,我还是会极简的实现一个 RPC 框架.帮助大家理解 RPC 框架的原理. 广义的来讲一个完整的 RPC 包含了很多组件,包括服务发现,服务治理,远程调用,调用链分析,网关等等.我将会慢慢的实现这些功能,这篇文章

经测试稳定可用的蓝牙链接通信Demo,记录过程中遇到的问题的思考和解决办法,并整理后给出一个Utils类可以简单调用来实现蓝牙功能

说明:这是本人在蓝牙开发过程中遇到过的问题记录和分析,以及解决办法. 在研究过程中,许多的前人给出的解决方案和思路指导对我相当有帮助,但并非都是可采取的解决方法, 经过本人对这些方法的测试和使用过后,给出自己的理解和解决方案,不一定是正确的,但这些方法的确可以解决问题. 如果有人遇到同样的问题,并且看到我的文章解决,那是我的荣幸. !!!!!!但特别需要说明的是,看的越多,不明白的越多,我的看法可能是完全错误的,这些方法只是暂时解决了我的问题, !!!!!!如果有人发现了我的错误,请私信或评论告

Coding之路——重新学习C++(7):用继承写出一个好类

1.继承类时需要注意的地方 (1)当一个类作为基类的时候,这个类就必须有定义. (2)在派生类中,基类的默认构造函数可以被隐式调用,但是如果基类的构造函数都有参数,派生类需要直接调用一个.派生类的构造函数只能描述派生类自己的成员变量和自己的基类的直接初始式,它不能直接初始化基类的成员. Manager::Manager(const string &n, int d, int lvl) :family_name(n), //错误:在Manager里没有family_name声明 departmen

[PY3]——找出一个序列中出现次数最多的元素/collections.Counter 类的用法

问题 怎样找出一个序列中出现次数最多的元素呢? 解决方案 collections.Counter 类就是专门为这类问题而设计的, 它甚至有一个有用的 most_common() 方法直接给了你答案 collections.Counter 类 1. most_common(n)统计top_n from collections import Counter words = [ 'look', 'into', 'my', 'eyes', 'look', 'into', 'my', 'eyes', 't