框架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)
至此一个框架的主要功能已经实现了
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律