框架2

写框架要API友好,让使用者使用,越简单越好

 手动实现wsgify装饰器

1
2
3
4
5
6
7
def wsgify(fn):
    def inner(environ, start_response):
        request = Request(environ)
        resp = fn(request)
        return resp(environ, start_response)
 
    return inner

  

使用正则表达式捕获

复制代码
Route = namedtuple('Route', ('pattern', 'method', 'handler'))


class Application:
    def __init__(self, **options):
        self.routes = []
        self.options = options

    def _route(self, rules, methods, handler):
        self.routes.append(Route(re.compile(rules), methods, handler))

    def router(self, pattern, methods=None):
        if methods is None:
            methods = ['GET', 'POST', 'HEAD', 'OPTION', 'DELETE', 'HEAD']

        def dec(fn):
            self._route(pattern, methods, fn)
            return fn
        return dec

    @wsgify
    def __call__(self, request):
        for route in self.routes:
            if request.method == route.method:
                m = route.pattern.match(request.path)
                if m:
                    request.args = m.groupdict()
                    return route.handler(request)

app = Application()


@app.router(r'^/hello/(?P<name>\w+)$', methods='GET')
def hello(request):
    name = request.args.get('name', 'unknown')
    body = "hello,world {}".format(name)
    return Response(body)
复制代码

也可以将参数直接handler里面

复制代码
Route = namedtuple('Route', ('pattern', 'method', 'handler'))


class Application:
    def __init__(self, **options):
        self.routes = []
        self.options = options

    def _route(self, rules, methods, handler):
        self.routes.append(Route(re.compile(rules), methods, handler))

    def router(self, pattern, methods=None):
        if methods is None:
            methods = ['GET', 'POST', 'HEAD', 'OPTION', 'DELETE', 'HEAD']

        def dec(fn):
            self._route(pattern, methods, fn)
            return fn

        return dec

    @wsgify
    def __call__(self, request):
        for route in self.routes:
            if request.method == route.method:
                m = route.pattern.match(request.path)
                if m:
                    return route.handler(request, **m.groupdict()) # 直接传给函数

app = Application()


@app.router(r'^/hello/(?P<name>\w+)$', methods='GET')
def hello(request, **kwargs):
    name = kwargs.get('name', 'unknown')
    body = "hello,world {}".format(name)
    return Response(body)
复制代码

子路由(根据三级域名来做不同的路由 flask里面的蓝图)  跳转不在路由这里

复制代码
Route = namedtuple('Route', ('pattern', 'method', 'handler'))

class Router:
    def __init__(self, prefix='', domian=None):
        self.routes = []
        self.prefix = prefix
        self.domain = domian

    def _route(self, rules, methods, handler):
        self.routes.append(Route(re.compile(rules), methods, handler))

    def router(self, pattern, methods=None):
        if methods is None:
            methods = ['GET', 'POST', 'HEAD', 'OPTION', 'DELETE', 'HEAD']

        def dec(fn):
            self._route(pattern, methods, fn)
            return fn
        return dec

    def match(self, request):
        if self.domain is None or re.match(self.domain, request.host):  # host只是得到 ip:port
            if request.path.startswith(self.prefix):
                for route in self.routes:
                    if request.method in route.method:
                        m = route.pattern.match(request.path.replace(self.prefix, '', 1)) # 去掉前缀
                        if m:
                            request.args = m.groupdict()
                            return route.handler


class Application:
    def __init__(self, **options):
        self.routes = []
        self.options = options

    def add_router(self, router):
        self.routes.append(router)

    @wsgify
    def __call__(self, request):
        for route in self.routes:
            handler = route.match(request)
            if handler:
                return handler(request)


r1 = Router(domian='python.magedu.com') # 这能以这个域名访问 不能是用IP访问
r2 = Router('/shop')


@r2.router(r'^/hello/(?P<name>\w+)$', methods='GET')
def hello(request):
    name = request.args.get('name', 'unknown')
    body = "hello,world {}".format(name)
    return Response(body)

app = Application()
app.add_router(r1)
复制代码

改进

复制代码
Route = namedtuple('Route', ('pattern', 'method', 'handler'))


class Router:
    def __init__(self, prefix='', domian=None):
        self.routes = []
        self.prefix = prefix
        self.domain = domian

    def _route(self, rules, methods, handler):
        self.routes.append(Route(re.compile(rules), methods, handler))

    def router(self, pattern, methods=None):
        if methods is None:
            methods = ['GET', 'POST', 'HEAD', 'OPTION', 'DELETE', 'HEAD']

        def dec(fn):
            self._route(pattern, methods, fn)
            return fn
        return dec
    
    def _domain_match(self,request):
        return self.domain is None or re.match(self.domain, request.host)

    def _prefix_match(self,request):
        return request.path.startswith(self.prefix)

    def match(self, request):
        if self._domain_match(request) and self._prefix_match(request): # 由于上面这里的层级太深,可以将判断的单独提取到另外的函数中进行判断
            for route in self.routes:
                if request.method in route.method:
                    m = route.pattern.match(request.path.replace(self.prefix, '', 1))
                    if m:
                        request.args = m.groupdict()
                        return route.handler


class Application:
    def __init__(self, **options):
        self.routes = []
        self.options = options

    def add_router(self, router):
        self.routes.append(router)

    @wsgify
    def __call__(self, request):
        for route in self.routes:
            handler = route.match(request)
            if handler:
                return handler(request)


r1 = Router() # domian='python.duan.com' 只能以这个域名访问 不能是用IP
r2 = Router('/shop')


@r2.router(r'^/hello/(?P<name>\w+)$', methods='GET')
def hello(request):
    name = request.args.get('name', 'unknown')
    body = "hello,world {}".format(name)
    return Response(body)
复制代码

 

复制代码
#  {name:int}
Route = namedtuple('Route', ('pattern', 'casts', 'method', 'handler'))

PATTERN = {
    'str': r'[^/].+',
    'word': r'\w+',
    'any': r'.+',
    'int': r'[+-]?\d+',
    'float': r'[+-]?\d+\.\d+'
}
CASTS = {
    'str': str,
    'word': str,
    'any': str,
    'int': int,
    'float': float
}


class Router:
    def __init__(self, prefix='', domian=None):
        self.routes = []
        self.prefix = prefix
        self.domain = domian

    def _route(self, rules, methods, handler):  # 只需要改变这里就行了
        pattern, casts = self._rule_parse(rules)
        self.routes.append(Route(re.compile(pattern), casts, methods, handler))

    def _rule_parse(self, rules):
        pattern = []
        spec = []
        casts = {}
        is_spec = False
        for c in rules:  # /hello/{name:str}/{id:int}
            if c == '{' and not is_spec:
                is_spec = True
            elif c == '}' and is_spec:
                is_spec = False
                name, pat, name_type = self._spec_parse(''.join(spec))  # 名字 原始正则 类型
                spec.clear()
                pattern.append(pat)
                casts[name] = name_type
            elif is_spec:
                spec.append(c)
            else:
                pattern.append(c)
        return '{}$'.format(''.join(pattern)), casts

    def _spec_parse(self, src):
        tmp = [x.strip() for x in src.split(':')]  # {name:str}
        if len(tmp) > 2:
            raise Exception('error')
        name = tmp[0]
        name_type = str
        if len(tmp) == 2:
            typed = tmp[1]
        if name_type not in CASTS.values():
            raise Exception("{} type is error".format(name_type))
        pattern = '(?P<{}>{})'.format(name, PATTERN[name_type])
        return name, pattern, CASTS[name_type]

    def router(self, pattern, methods=None):
        if methods is None:
            methods = ['GET', 'POST', 'HEAD', 'OPTION', 'DELETE', 'HEAD']

        def dec(fn):
            self._route(pattern, methods, fn)
            return fn
        return dec

    def _domain_match(self, request):
        return self.domain is None or re.match(self.domain, request.host)

    def _prefix_match(self, request):
        return request.path.startswith(self.prefix)

    def match(self, request):
        if self._domain_match(request) and self._prefix_match(request):
            for route in self.routes:
                if request.method in route.method:
                    m = route.pattern.match(request.path.replace(self.prefix, '', 1))
                    if m:
                        print(m.groupdict().items(),route.casts)
                        request.args = {}
                        for k, v in m.groupdict().items():
                            request.args[k] = route.casts[k](v)  # name type
                        return route.handler


class Application:
    def __init__(self, **options):
        self.routes = []
        self.options = options

    def add_router(self, router):
        self.routes.append(router)

    @wsgify
    def __call__(self, request):
        for route in self.routes:
            handler = route.match(request)
            if handler:
                return handler(request)
        return exc.HTTPNotFound('NOT')


r1 = Router('')  # domian='python.magedu.com' 这能以这个域名访问 不能是用IP
r2 = Router('/shop')


@r2.router(r'^/hello/{name:str}$', methods='GET')
def hello(request):
    name = request.args.get('name', 'unknown')
    body = "hello,world {}".format(name)
    return Response(body)
复制代码

至此一个框架的主要功能已经实现了

 

posted @   escapist  阅读(183)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示