轉載請注明: TheV 先看下tornado的風格 請求時/main時,如果請求方法是post,則執(zhí)行MainHandler里面的post方法。。。。。 這樣感覺用著很不習慣。就只有 "GET", "HEAD", "POST", "DELETE", "PATCH", "PUT", 這幾個方法,而且還不是由url體現(xiàn)出來的。 說下我的做法。由于是hack tornado的源碼,所以有必要簡單看下tornado的運行流程。 主要是在web.py里面。這里不討論tornado是怎么實現(xiàn)一個高性能,非阻塞的 http 服務器,只簡單說下他是怎么匹配映射然后執(zhí)行的。 Application類 __init__()里面就是保存設置,設置默認。注意里面有個add_handlers()。 進入add_handlers(),里面主要是對每個映射規(guī)則創(chuàng)建一個URLSpec類。這個類是專門保存映射規(guī)則和實例化handler類的,是hack的重點對象,這個后面會講到。 然后就是__call__(self, request),這個可以理解為請求的入口,里面注釋寫的很詳細了。 關于__call__和__init__ ,可以看下http://stackoverflow.com/questions/9663562/what-is-difference-between-init-and-call-in-python。 URLSpec類 回到__call__里面的最后handler._execute(transforms,spec, *args, **kwargs)。 這里我在_execute加了spec,為了在后面執(zhí)行handler類中方法時用到保存在spec中的action. _execute在RequestHandler類中 最后的效果類似于 可以看到,路由規(guī)則并不是完全的ruby on rail那種,還是要自己寫需要指定映射的方法,框架只會幫你映射到類。 這樣的好處就是保有一定的靈活性。比如,像一般的登陸頁面,鏈接是'/login'就可以了,沒必要非要弄成"user/login". tornado的源碼算是屬于很少很少的那種了。把復雜問題變簡單,這就是facebook工程師的境界。 最后附上例子 http://files.VEVb.com/TheViper/python_rails_style.zip 基于tornado 3.2.2 有個問題需要注意下,我用的是sublime text3,它是基于python3.3的,電腦裝的是python 2.7.運行的時候卻必須是print()的寫法才可以,否則報錯。。不知道是什么原因。 有知道的朋友請告訴我一聲。 import tornado.ioloopimport tornado.webclass MainHandler(tornado.web.RequestHandler): def get(self): self.write('<html><body><form action="/" method="post">' '<input type="text" name="message">' '<input type="submit" value="Submit">' '</form></body></html>') def post(self): self.set_header("Content-Type", "text/plain") self.write("You wrote " + self.get_argument("message"))application = tornado.web.Application([ (r"/main", MainHandler),])if __name__ == "__main__": application.listen(8888) tornado.ioloop.IOLoop.instance().start()
"OPTIONS"class Application(object): def __init__(self, handlers=None, default_host="", transforms=None, wsgi=False, **settings): if transforms is None: self.transforms = [] if settings.get("gzip"): self.transforms.append(GZipContentEncoding) self.transforms.append(ChunkedTransferEncoding) else: self.transforms = transforms #保存配置handlers中處理的類,此時列表中的類還沒實例化 self.handlers = [] self.named_handlers = {} self.default_host = default_host self.settings = settings self.ui_modules = {'linkify': _linkify, 'xsrf_form_html': _xsrf_form_html, 'Template': TemplateModule, } self.ui_methods = {} self._wsgi = wsgi self._load_ui_modules(settings.get("ui_modules", {})) self._load_ui_methods(settings.get("ui_methods", {})) if self.settings.get("static_path"): path = self.settings["static_path"] handlers = list(handlers or []) static_url_PRefix = settings.get("static_url_prefix", "/static/") static_handler_class = settings.get("static_handler_class", StaticFileHandler) static_handler_args = settings.get("static_handler_args", {}) static_handler_args['path'] = path for pattern in [re.escape(static_url_prefix) + r"(.*)", r"/(favicon/.ico)", r"/(robots/.txt)"]: handlers.insert(0, (pattern, static_handler_class, static_handler_args)) if handlers: self.add_handlers(".*$", handlers) if self.settings.get('debug'): self.settings.setdefault('autoreload', True) self.settings.setdefault('compiled_template_cache', False) self.settings.setdefault('static_hash_cache', False) self.settings.setdefault('serve_traceback', True) # Automatically reload modified modules if self.settings.get('autoreload') and not wsgi: from tornado import autoreload autoreload.start() def listen(self, port, address="", **kwargs): # import is here rather than top level because HTTPServer # is not importable on appengine #開啟服務器監(jiān)聽 from tornado.httpserver import HTTPServer server = HTTPServer(self, **kwargs) server.listen(port, address) def add_handlers(self, host_pattern, host_handlers): if not host_pattern.endswith("$"): host_pattern += "$" handlers = [] # The handlers with the wildcard host_pattern are a special # case - they're added in the constructor but should have lower # precedence than the more-precise handlers added later. # If a wildcard handler group exists, it should always be last # in the list, so insert new groups just before it. if self.handlers and self.handlers[-1][0].pattern == '.*$': self.handlers.insert(-1, (re.compile(host_pattern), handlers)) else: self.handlers.append((re.compile(host_pattern), handlers)) for spec in host_handlers: if isinstance(spec, (tuple, list)): assert len(spec) in (2, 3, 4) #創(chuàng)建映射url與handler的類,URLSpec類中有實例過的handler spec = URLSpec(*spec) #添加 handlers.append(spec) if spec.name: if spec.name in self.named_handlers: app_log.warning( "Multiple handlers named %s; replacing previous value", spec.name) self.named_handlers[spec.name] = spec def add_transform(self, transform_class): self.transforms.append(transform_class) def __call__(self, request): """Called by HTTPServer to execute the request.""" #請求從這里進入 transforms = [t(request) for t in self.transforms] handler = None args = [] kwargs = {} handlers = self._get_host_handlers(request) if not handlers: handler = RedirectHandler( self, request, url="http://" + self.default_host + "/") else: #例子走這里 for spec in handlers: #遍歷,依次匹配 match = spec.regex.match(request.path) #匹配成功 if match: #實例過的handler handler = spec.handler_class(self, request,*args,**kwargs) if spec.regex.groups: # None-safe wrapper around url_unescape to handle # unmatched optional groups correctly def unquote(s): if s is None: return s return escape.url_unescape(s, encoding=None, plus=False) # Pass matched groups to the handler. Since # match.groups() includes both named and unnamed groups, # we want to use either groups or groupdict but not both. # Note that args are passed as bytes so the handler can # decide what encoding to use. if spec.regex.groupindex: kwargs = dict( (str(k), unquote(v)) for (k, v) in match.groupdict().items()) else: args = [unquote(s) for s in match.groups()] break if not handler: if self.settings.get('default_handler_class'): handler_class = self.settings['default_handler_class'] handler_args = self.settings.get( 'default_handler_args', {}) else: handler_class = ErrorHandler handler_args = dict(status_code=404) #不會走這里 handler = handler_class(self, request, **handler_args) # If template cache is disabled (usually in the debug mode), # re-compile templates and reload static files on every # request so you don't need to restart to see changes if not self.settings.get("compiled_template_cache", True): with RequestHandler._template_loader_lock: for loader in RequestHandler._template_loaders.values(): loader.reset() if not self.settings.get('static_hash_cache', True): StaticFileHandler.reset() #準備開始執(zhí)行類中的方法 if issubclass(spec.handler_class,tornado.websocket.WebSocketHandler): #如果handler_class是websocket的子類,這里必須要這個,否則 #不能用websocket,因為websocket里面重寫了_execute方法 handler._execute(transforms,*args, **kwargs) else: handler._execute(transforms,spec,*args, **kwargs) return handler
class URLSpec(object): """Specifies mappings between URLs and handlers.""" def __init__(self, pattern, handler, kwargs=None, name=None): """Parameters: * ``pattern``: Regular expression to be matched. Any groups in the regex will be passed in to the handler's get/post/etc methods as arguments. * ``handler_class``: `RequestHandler` subclass to be invoked. * ``kwargs`` (optional): A dictionary of additional arguments to be passed to the handler's constructor. * ``name`` (optional): A name for this handler. Used by `Application.reverse_url`. """ if not pattern.endswith('$'): pattern += '$' self.regex = re.compile(pattern) assert len(self.regex.groupindex) in (0, self.regex.groups), / ("groups in url regexes must either be all named or all " "positional: %r" % self.regex.pattern) if isinstance(handler, str): # import the Module and instantiate the class # Must be a fully qualified name (module.ClassName) #實例化handler類 handler = import_object(handler) #保存action self.action=None #如果配置中設置了action if type(kwargs) is dict and 'action' in kwargs: self.action=kwargs['action'] self.handler_class = handler self.kwargs = kwargs or {} self.name = name self._path, self._group_count = self._find_groups()
def _execute(self, transforms,spec, *args, **kwargs): """Executes this request with the given output transforms.""" self._transforms = transforms try: if self.request.method not in self.SUPPORTED_METHODS: raise HTTPError(405) self.path_args = [self.decode_argument(arg) for arg in args] self.path_kwargs = dict((k, self.decode_argument(v, name=k)) for (k, v) in kwargs.items()) # If XSRF cookies are turned on, reject form submissions without # the proper cookie if self.request.method not in ("GET", "HEAD", "OPTIONS") and / self.application.settings.get("xsrf_cookies"): self.check_xsrf_cookie() #設置當前的action,后面會用到 self.current_action=spec.action #如果設置了隱藏參數(shù) if 'param_keys' in spec.kwargs: #將隱藏參數(shù)和實際請求中的參數(shù)一一對應 self.params=dict(zip(spec.kwargs['param_keys'],self.path_args)); self._when_complete(self.prepare(), self._execute_method) except Exception as e: self._handle_request_exception(e) def _when_complete(self, result, callback): try: #不是長連接,走這里,執(zhí)行下面的_execute_method(self) if result is None: callback() elif isinstance(result, Future): if result.done(): if result.result() is not None: raise ValueError('Expected None, got %r' % result.result()) callback() else: # Delayed import of IOLoop because it's not available # on app engine from tornado.ioloop import IOLoop IOLoop.current().add_future( result, functools.partial(self._when_complete, callback=callback)) else: raise ValueError("Expected Future or None, got %r" % result) except Exception as e: self._handle_request_exception(e) def _execute_method(self): if not self._finished: #默認的action是請求方法 method = getattr(self, self.request.method.lower()) if self.current_action: #變成我的action method = getattr(self, self.current_action) #執(zhí)行 self._when_complete(method(*self.path_args, **self.path_kwargs), self._execute_finish)

新聞熱點
疑難解答