Source code for hyper2web.router

from .abstract import AbstractRouter
from .exceptions import RouteNotRegisteredException
from .http import HTTP, Stream, Request, Response


[docs]class Router(AbstractRouter): """User should never construct Router""" # todo: I may want to change the constructor def __init__(self, default_get): """ :param default_get: should be a handler function """ self._routes = { 'GET': {}, 'POST': {} } self.default_get = default_get
[docs] def register(self, method: str, route: str, handler): assert method in ['GET', 'POST'] # 这只是目前为了测试而加的限制 self._routes[method][route] = handler
[docs] def find_match(self, path: str): """ 'user/{userId}' should match 'user/abc' userId = abc return a tuple (matched, parameters) matched is the route which matches the incoming path parameters is a dict of parameters and their values """ # todo: now the problem is how to implement it # todo: pattern matching should be independent from :method, # todo: but the current implementation doesn't support it. Should improve it later. for routes_of_this_method in self._routes.values(): for route in routes_of_this_method: matched, parameters = self._match(route, path) # this function returns the first match, not the best match if matched: return route, parameters # did not find any route which matches the path return None, None
@classmethod def _match(cls, route, path): # '/something/xxx/' to 'something/xxx'. Get rid of '/' at the left and the right end of a string route = route.lstrip('/').rstrip('/').split('/') path = path.lstrip('/').rstrip('/').split('/') if len(route) != len(path): return False, None else: # todo: optimize the logic parameters = {} for r, p in zip(route, path): if r == p == '': return True, None if r == '' and r != p: return False, None if r[0] == '{' and r[-1] == '}': parameters[r[1:-1]] = p elif r != p: return False, None return True, parameters # async
[docs] async def handle_route(self, http: HTTP, stream: Stream): path = stream.headers[':path'] method = stream.headers[':method'] route, parameters = self.find_match(path) # 如果没有任何匹配,就默认为静态文件读取 if route is None: if method == 'GET': handler = self.default_get else: handler = None else: handler = self._routes[method].get(route, None) if handler is not None: req = Request(stream, parameters) res = Response(stream.stream_id, http) await handler(req, res) else: raise RouteNotRegisteredException(path + ' is not a registered route')