ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、视频、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
### Navigation - [index](# "General Index") - [modules](# "Python Module Index") | - [next](# "Templates and UI") | - [previous](# "Queue example - a concurrent web spider") | - [Tornado 4.4.dev1 documentation](#) » - [User's guide](#) » # Structure of a Tornado web application A Tornado web application generally consists of one or more[`RequestHandler`](# "tornado.web.RequestHandler") subclasses, an [`Application`](# "tornado.web.Application") object whichroutes incoming requests to handlers, and a `main()` functionto start the server. A minimal “hello world” example looks something like this: ~~~ import tornado.ioloop import tornado.web class MainHandler(tornado.web.RequestHandler): def get(self): self.write("Hello, world") def make_app(): return tornado.web.Application([ (r"/", MainHandler), ]) if __name__ == "__main__": app = make_app() app.listen(8888) tornado.ioloop.IOLoop.current().start() ~~~ ### The `Application` object The [`Application`](# "tornado.web.Application") object is responsible for global configuration, includingthe routing table that maps requests to handlers. The routing table is a list of [`URLSpec`](# "tornado.web.URLSpec") objects (or tuples), each ofwhich contains (at least) a regular expression and a handler class.Order matters; the first matching rule is used. If the regularexpression contains capturing groups, these groups are the *patharguments* and will be passed to the handler's HTTP method. If adictionary is passed as the third element of the [`URLSpec`](# "tornado.web.URLSpec"), itsupplies the *initialization arguments* which will be passed to[`RequestHandler.initialize`](# "tornado.web.RequestHandler.initialize"). Finally, the [`URLSpec`](# "tornado.web.URLSpec") may have aname, which will allow it to be used with[`RequestHandler.reverse_url`](# "tornado.web.RequestHandler.reverse_url"). For example, in this fragment the root URL `/` is mapped to`MainHandler` and URLs of the form `/story/` followed by a numberare mapped to `StoryHandler`. That number is passed (as a string) to`StoryHandler.get`. ~~~ class MainHandler(RequestHandler): def get(self): self.write('<a href="%s">link to story 1</a>' % self.reverse_url("story", "1")) class StoryHandler(RequestHandler): def initialize(self, db): self.db = db def get(self, story_id): self.write("this is story %s" % story_id) app = Application([ url(r"/", MainHandler), url(r"/story/([0-9]+)", StoryHandler, dict(db=db), name="story") ]) ~~~ The [`Application`](# "tornado.web.Application") constructor takes many keyword arguments thatcan be used to customize the behavior of the application and enableoptional features; see [`Application.settings`](# "tornado.web.Application.settings") for the complete list. ### Subclassing `RequestHandler` Most of the work of a Tornado web application is done in subclassesof [`RequestHandler`](# "tornado.web.RequestHandler"). The main entry point for a handler subclassis a method named after the HTTP method being handled: `get()`,`post()`, etc. Each handler may define one or more of these methodsto handle different HTTP actions. As described above, these methodswill be called with arguments corresponding to the capturing groupsof the routing rule that matched. Within a handler, call methods such as [`RequestHandler.render`](# "tornado.web.RequestHandler.render") or[`RequestHandler.write`](# "tornado.web.RequestHandler.write") to produce a response. `render()` loads a[`Template`](# "tornado.template.Template") by name and renders it with the givenarguments. `write()` is used for non-template-based output; itaccepts strings, bytes, and dictionaries (dicts will be encoded asJSON). Many methods in [`RequestHandler`](# "tornado.web.RequestHandler") are designed to be overridden insubclasses and be used throughout the application. It is commonto define a `BaseHandler` class that overrides methods such as[`write_error`](# "tornado.web.RequestHandler.write_error") and [`get_current_user`](# "tornado.web.RequestHandler.get_current_user")and then subclass your own `BaseHandler` instead of [`RequestHandler`](# "tornado.web.RequestHandler")for all your specific handlers. ### Handling request input The request handler can access the object representing the currentrequest with `self.request`. See the class definition for[`HTTPServerRequest`](# "tornado.httputil.HTTPServerRequest") for a complete list ofattributes. Request data in the formats used by HTML forms will be parsed for youand is made available in methods like [`get_query_argument`](# "tornado.web.RequestHandler.get_query_argument")and [`get_body_argument`](# "tornado.web.RequestHandler.get_body_argument"). ~~~ class MyFormHandler(tornado.web.RequestHandler): def get(self): self.write('<html><body><form action="/myform" 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_body_argument("message")) ~~~ Since the HTML form encoding is ambiguous as to whether an argument isa single value or a list with one element, [`RequestHandler`](# "tornado.web.RequestHandler") hasdistinct methods to allow the application to indicate whether or notit expects a list. For lists, use[`get_query_arguments`](# "tornado.web.RequestHandler.get_query_arguments") and[`get_body_arguments`](# "tornado.web.RequestHandler.get_body_arguments") instead of their singularcounterparts. Files uploaded via a form are available in `self.request.files`,which maps names (the name of the HTML `<input type="file">`element) to a list of files. Each file is a dictionary of the form`{"filename":..., "content_type":..., "body":...}`. The `files`object is only present if the files were uploaded with a form wrapper(i.e. a `multipart/form-data` Content-Type); if this format was not usedthe raw uploaded data is available in `self.request.body`.By default uploaded files are fully buffered in memory; if you need tohandle files that are too large to comfortably keep in memory see the[`stream_request_body`](# "tornado.web.stream_request_body") class decorator. Due to the quirks of the HTML form encoding (e.g. the ambiguity aroundsingular versus plural arguments), Tornado does not attempt to unifyform arguments with other types of input. In particular, we do notparse JSON request bodies. Applications that wish to use JSON insteadof form-encoding may override [`prepare`](# "tornado.web.RequestHandler.prepare") to parse theirrequests: ~~~ def prepare(self): if self.request.headers["Content-Type"].startswith("application/json"): self.json_args = json.loads(self.request.body) else: self.json_args = None ~~~ ### Overriding RequestHandler methods In addition to `get()`/`post()`/etc, certain other methods in[`RequestHandler`](# "tornado.web.RequestHandler") are designed to be overridden by subclasses whennecessary. On every request, the following sequence of calls takesplace: 1. A new [`RequestHandler`](# "tornado.web.RequestHandler") object is created on each request 1. [`initialize()`](# "tornado.web.RequestHandler.initialize") is called with the initializationarguments from the [`Application`](# "tornado.web.Application") configuration. `initialize`should typically just save the arguments passed into membervariables; it may not produce any output or call methods like[`send_error`](# "tornado.web.RequestHandler.send_error"). 1. [`prepare()`](# "tornado.web.RequestHandler.prepare") is called. This is most useful in abase class shared by all of your handler subclasses, as `prepare`is called no matter which HTTP method is used. `prepare` mayproduce output; if it calls [`finish`](# "tornado.web.RequestHandler.finish") (or`redirect`, etc), processing stops here. 1. One of the HTTP methods is called: `get()`, `post()`, `put()`,etc. If the URL regular expression contains capturing groups, theyare passed as arguments to this method. 1. When the request is finished, [`on_finish()`](# "tornado.web.RequestHandler.on_finish") iscalled. For synchronous handlers this is immediately after`get()` (etc) return; for asynchronous handlers it is after thecall to [`finish()`](# "tornado.web.RequestHandler.finish"). All methods designed to be overridden are noted as such in the[`RequestHandler`](# "tornado.web.RequestHandler") documentation. Some of the most commonlyoverridden methods include: - [`write_error`](# "tornado.web.RequestHandler.write_error") -outputs HTML for use on error pages. - [`on_connection_close`](# "tornado.web.RequestHandler.on_connection_close") - called when the clientdisconnects; applications may choose to detect this case and haltfurther processing. Note that there is no guarantee that a closedconnection can be detected promptly. - [`get_current_user`](# "tornado.web.RequestHandler.get_current_user") - see [User authentication](#) - [`get_user_locale`](# "tornado.web.RequestHandler.get_user_locale") - returns [`Locale`](# "tornado.locale.Locale") object to usefor the current user - [`set_default_headers`](# "tornado.web.RequestHandler.set_default_headers") - may be used to setadditional headers on the response (such as a custom `Server`header) ### Error Handling If a handler raises an exception, Tornado will call[`RequestHandler.write_error`](# "tornado.web.RequestHandler.write_error") to generate an error page.[`tornado.web.HTTPError`](# "tornado.web.HTTPError") can be used to generate a specified statuscode; all other exceptions return a 500 status. The default error page includes a stack trace in debug mode and aone-line description of the error (e.g. “500: Internal Server Error”)otherwise. To produce a custom error page, override[`RequestHandler.write_error`](# "tornado.web.RequestHandler.write_error") (probably in a base class shared by allyour handlers). This method may produce output normally viamethods such as [`write`](# "tornado.web.RequestHandler.write") and [`render`](# "tornado.web.RequestHandler.render").If the error was caused by an exception, an `exc_info` triple willbe passed as a keyword argument (note that this exception is notguaranteed to be the current exception in [`sys.exc_info`](https://docs.python.org/3.4/library/sys.html#sys.exc_info "(in Python v3.4)") [https://docs.python.org/3.4/library/sys.html#sys.exc_info], so`write_error` must use e.g. [`traceback.format_exception`](https://docs.python.org/3.4/library/traceback.html#traceback.format_exception "(in Python v3.4)") [https://docs.python.org/3.4/library/traceback.html#traceback.format_exception] instead of[`traceback.format_exc`](https://docs.python.org/3.4/library/traceback.html#traceback.format_exc "(in Python v3.4)") [https://docs.python.org/3.4/library/traceback.html#traceback.format_exc]). It is also possible to generate an error page from regular handlermethods instead of `write_error` by calling[`set_status`](# "tornado.web.RequestHandler.set_status"), writing a response, and returning.The special exception [`tornado.web.Finish`](# "tornado.web.Finish") may be raised to terminatethe handler without calling `write_error` in situations where simplyreturning is not convenient. For 404 errors, use the `default_handler_class`[`Application setting`](# "tornado.web.Application.settings"). This handler should override[`prepare`](# "tornado.web.RequestHandler.prepare") instead of a more specific method like`get()` so it works with any HTTP method. It should produce itserror page as described above: either by raising a `HTTPError(404)`and overriding `write_error`, or calling `self.set_status(404)`and producing the response directly in `prepare()`. ### Redirection There are two main ways you can redirect requests in Tornado:[`RequestHandler.redirect`](# "tornado.web.RequestHandler.redirect") and with the [`RedirectHandler`](# "tornado.web.RedirectHandler"). You can use `self.redirect()` within a [`RequestHandler`](# "tornado.web.RequestHandler") method toredirect users elsewhere. There is also an optional parameter`permanent` which you can use to indicate that the redirection isconsidered permanent. The default value of `permanent` is`False`, which generates a `302 Found` HTTP response code and isappropriate for things like redirecting users after successful`POST` requests. If `permanent` is true, the `301 MovedPermanently` HTTP response code is used, which is useful fore.g. redirecting to a canonical URL for a page in an SEO-friendlymanner. [`RedirectHandler`](# "tornado.web.RedirectHandler") lets you configure redirects directly in your[`Application`](# "tornado.web.Application") routing table. For example, to configure a singlestatic redirect: ~~~ app = tornado.web.Application([ url(r"/app", tornado.web.RedirectHandler, dict(url="http://itunes.apple.com/my-app-id")), ]) ~~~ [`RedirectHandler`](# "tornado.web.RedirectHandler") also supports regular expression substitutions.The following rule redirects all requests beginning with `/pictures/`to the prefix `/photos/` instead: ~~~ app = tornado.web.Application([ url(r"/photos/(.*)", MyPhotoHandler), url(r"/pictures/(.*)", tornado.web.RedirectHandler, dict(url=r"/photos/\1")), ]) ~~~ Unlike [`RequestHandler.redirect`](# "tornado.web.RequestHandler.redirect"), [`RedirectHandler`](# "tornado.web.RedirectHandler") uses permanentredirects by default. This is because the routing table does not changeat runtime and is presumed to be permanent, while redirects found inhandlers are likely to be the result of other logic that may change.To send a temporary redirect with a [`RedirectHandler`](# "tornado.web.RedirectHandler"), add`permanent=False` to the [`RedirectHandler`](# "tornado.web.RedirectHandler") initialization arguments. ### Asynchronous handlers Tornado handlers are synchronous by default: when the`get()`/`post()` method returns, the request is consideredfinished and the response is sent. Since all other requests areblocked while one handler is running, any long-running handler shouldbe made asynchronous so it can call its slow operations in anon-blocking way. This topic is covered in more detail in[*Asynchronous and non-Blocking I/O*](#); this section is about the particulars ofasynchronous techniques in [`RequestHandler`](# "tornado.web.RequestHandler") subclasses. The simplest way to make a handler asynchronous is to use the[`coroutine`](# "tornado.gen.coroutine") decorator. This allows you to perform non-blocking I/Owith the `yield` keyword, and no response will be sent until thecoroutine has returned. See [*Coroutines*](#) for more details. In some cases, coroutines may be less convenient than acallback-oriented style, in which case the [`tornado.web.asynchronous`](# "tornado.web.asynchronous")decorator can be used instead. When this decorator is used the responseis not automatically sent; instead the request will be kept open untilsome callback calls [`RequestHandler.finish`](# "tornado.web.RequestHandler.finish"). It is up to the applicationto ensure that this method is called, or else the user's browser willsimply hang. Here is an example that makes a call to the FriendFeed API usingTornado's built-in [`AsyncHTTPClient`](# "tornado.httpclient.AsyncHTTPClient"): ~~~ class MainHandler(tornado.web.RequestHandler): @tornado.web.asynchronous def get(self): http = tornado.httpclient.AsyncHTTPClient() http.fetch("http://friendfeed-api.com/v2/feed/bret", callback=self.on_response) def on_response(self, response): if response.error: raise tornado.web.HTTPError(500) json = tornado.escape.json_decode(response.body) self.write("Fetched " + str(len(json["entries"])) + " entries " "from the FriendFeed API") self.finish() ~~~ When `get()` returns, the request has not finished. When the HTTPclient eventually calls `on_response()`, the request is still open,and the response is finally flushed to the client with the call to`self.finish()`. For comparison, here is the same example using a coroutine: ~~~ class MainHandler(tornado.web.RequestHandler): @tornado.gen.coroutine def get(self): http = tornado.httpclient.AsyncHTTPClient() response = yield http.fetch("http://friendfeed-api.com/v2/feed/bret") json = tornado.escape.json_decode(response.body) self.write("Fetched " + str(len(json["entries"])) + " entries " "from the FriendFeed API") ~~~ For a more advanced asynchronous example, take a look at the [chatexample application](https://github.com/tornadoweb/tornado/tree/stable/demos/chat) [https://github.com/tornadoweb/tornado/tree/stable/demos/chat], whichimplements an AJAX chat room using [long polling](http://en.wikipedia.org/wiki/Push_technology#Long_polling) [http://en.wikipedia.org/wiki/Push_technology#Long_polling]. Usersof long polling may want to override `on_connection_close()` toclean up after the client closes the connection (but see that method'sdocstring for caveats). © Copyright 2009-2016, The Tornado Authors. Created using [Sphinx](http://sphinx-doc.org/) 1.3.5.