Foundations of Tornado Web Development

Creating a Basic Tornado Web Application

To develop a simple Tornado web application, inherit from tornado.web.RequestHandler and override the relevant HTTP method handlers. Initialize the application, define URL mappings, start the server, and begin the I/O loop.

from tornado import web, ioloop

class IndexHandler(web.RequestHandler):
    async def get(self):
        self.write("Hello, World!")

if __name__ == "__main__":
    app = web.Application([
        (r"/", IndexHandler)
    ], debug=True)
    app.listen(8888)
    ioloop.IOLoop.current().start()

Note: Enabling debug=True starts a background thread for auto-reloading. Terminate this thread via system resource managers if needed.

Access http://localhost:8888/ to view the output.

Avoiding Synchronous Methods in Tornado

Synchronous I/O operaitons block the event loop, delaying other requests:

import time
from tornado import web, ioloop

class BlockingHandler(web.RequestHandler):
    async def get(self):
        time.sleep(5)  # Synchronous call blocks event loop
        self.write("Delayed response")

class NonBlockingHandler(web.RequestHandler):
    async def get(self):
        self.write("Immediate response")

app = web.Application([
    (r"/blocking", BlockingHandler),
    (r"/nonblocking", NonBlockingHandler)
], debug=True)
app.listen(8888)
ioloop.IOLoop.current().start()

Consecutive requests to /blocking and /nonblocking demonstrate blocking behavior. Always use asynchronous I/O in Tornado handlers.

URL Configuration and Named Routes

Use tornado.web.URLSpec for named routes and parameter handling:

from tornado.web import URLSpec

people_config = {"db_name": "people"}

urls = [
    URLSpec(r"/", IndexHandler, name="home"),
    URLSpec(r"/people/(\d+)/?", PeopleDetailHandler, people_config, name="people_detail"),
]

Redirect using named routes:

self.redirect(self.reverse_url("people_detail", user_id))

Pass initial data to handlers via initialize:

class PeopleDetailHandler(web.RequestHandler):
    def initialize(self, db_name):
        self.db = db_name

    async def get(self, user_id):
        print(self.db)
        self.redirect(self.reverse_url("people_name", "example"))

Complete example:

from tornado import web, ioloop
from tornado.web import URLSpec

class IndexHandler(web.RequestHandler):
    async def get(self):
        self.write("Home")

class PeopleDetailHandler(web.RequestHandler):
    def initialize(self, db_name):
        self.db = db_name

    async def get(self, user_id):
        print(f"Database: {self.db}")
        self.redirect(self.reverse_url("people_name", "sample_name"))

class PeopleNameHandler(web.RequestHandler):
    async def get(self, name):
        self.write(f"Name: {name}")

class PeopleInfoHandler(web.RequestHandler):
    async def get(self, name, age, gender):
        self.write(f"Name: {name}, Age: {age}, Gender: {gender}")

people_config = {"db_name": "people"}
urls = [
    URLSpec(r"/", IndexHandler, name="home"),
    URLSpec(r"/people/(\d+)/?", PeopleDetailHandler, people_config, name="people_detail"),
    URLSpec(r"/people/(\w+)/?", PeopleNameHandler, name="people_name"),
    URLSpec(r"/people/(?P<name>\w+)/(?P<age>\d+)/(?P<gender>\w+)/?", 
            PeopleInfoHandler, name="people_info"),
]

if __name__ == "__main__":
    app = web.Application(urls, debug=True)
    app.listen(8888)
    ioloop.IOLoop.current().start()

Configuration with Options

Use define, options, parse_command_line, and parse_config_file for runtime configuration:

conf.cfg:

port=8002
debug=True

Application code:

from tornado import web, ioloop, options

define('port', default=8008, help="Listening port", type=int)
define('debug', default=True, help="Debug mode", type=bool)

options.parse_config_file("conf.cfg")

class MainHandler(web.RequestHandler):
    async def get(self):
        self.write("Hello")

if __name__ == "__main__":
    app = web.Application([(r"/", MainHandler)], debug=options.debug)
    app.listen(options.port)
    ioloop.IOLoop.current().start()

Run with: python app.py --port=8002

RequestHandler Utilities

Key methods:

  • initialize(): Constructor-like initialization
  • prepare(): Pre-request setup
  • on_finish(): Post-request cleanup
  • write_error(status_code, **kwargs): Custom error handling

Example:

import traceback

class CustomHandler(web.RequestHandler):
    def initialize(self, db):
        self.db = db

    def prepare(self):
        pass  # Pre-request logic

    def on_finish(self):
        pass  # Cleanup

    def write_error(self, status_code, **kwargs):
        self.write("Custom error page\n")
        if "exc_info" in kwargs:
            self.write("Details:\n")
            for line in traceback.format_exception(*kwargs["exc_info"]):
                self.write(line)
        self.finish()

Parameter Handling

  • get_argument(name): Single value
  • get_arguments(name): List of values
  • request.arguments: All parameters
  • JSON body: json.loads(self.request.body)

Response Control

  • finish(): Ends response, optionally with data
  • set_status(code): HTTP status code

Handler Subclasses

  • RedirectHandler: Permanent redirects
  • StaticFileHandler: Serve static files

Example:

from tornado.web import StaticFileHandler, RedirectHandler

app = web.Application([
    (r"/old", RedirectHandler, {"url": "/new"}),
    (r"/static/(.*)", StaticFileHandler, {"path": "/var/www/static"})
])

Template Rendering

Configure templtae path:

settings = {
    "template_path": "templates",
    "static_path": "/path/to/static",
    "static_url_prefix": "/static/"
}

app = web.Application(handlers, **settings)

Render templates with data:

class TemplateHandler(web.RequestHandler):
    async def get(self):
        greeting = "Welcome"
        self.render("hello.html", greeting=greeting)

templates/hello.html:

<h1>{{ greeting }}</h1>

Template features:

  • Variables: {{ var }}
  • Control structures: {% if %}...{% end %}, {% for %}...{% end %}
  • Built-in functions: escape, url_escape, json_encode, squeeze
  • Raw output: {% raw variable %}

Tags: tornado python web-development asynchronous Templates

Posted on Mon, 11 May 2026 03:42:09 +0000 by thepip3r