简体   繁体   中英

is there a better way to handle index.html with Tornado?


I want to know if there is a better way to handle my index.html file with Tornado.

I use StaticFileHandler for all the request,and use a specific MainHandler to handle my main request. If I only use StaticFileHandler I got a 403: Forbidden error

GET http://localhost:9000/
WARNING:root:403 GET / (127.0.0.1):  is not a file

here how I doing now:

import os
import tornado.ioloop
import tornado.web
from  tornado import web

__author__ = 'gvincent'

root = os.path.dirname(__file__)
port = 9999

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        try:
            with open(os.path.join(root, 'index.html')) as f:
                self.write(f.read())
        except IOError as e:
            self.write("404: Not Found")

application = tornado.web.Application([
    (r"/", MainHandler),
    (r"/(.*)", web.StaticFileHandler, dict(path=root)),
    ])

if __name__ == '__main__':
    application.listen(port)
    tornado.ioloop.IOLoop.instance().start()

Turns out that Tornado's StaticFileHandler already includes default filename functionality.

Feature was added in Tornado release 1.2.0: https://github.com/tornadoweb/tornado/commit/638a151d96d681d3bdd6ba5ce5dcf2bd1447959c

To specify a default file name you need to set the "default_filename" parameter as part of the WebStaticFileHandler initialization.

Updating your example:

import os
import tornado.ioloop
import tornado.web

root = os.path.dirname(__file__)
port = 9999

application = tornado.web.Application([
    (r"/(.*)", tornado.web.StaticFileHandler, {"path": root, "default_filename": "index.html"})
])

if __name__ == '__main__':
    application.listen(port)
    tornado.ioloop.IOLoop.instance().start()

This handles root requests:

  • / -> /index.html

sub-directory requests:

  • /tests/ -> /tests/index.html

and appears to correctly handle redirects for directories, which is nice:

  • /tests -> /tests/index.html

Thanks to the previous answer, here is the solution I prefer:

import Settings
import tornado.web
import tornado.httpserver


class Application(tornado.web.Application):
    def __init__(self):
        handlers = [
            (r"/", MainHandler)
        ]
        settings = {
            "template_path": Settings.TEMPLATE_PATH,
            "static_path": Settings.STATIC_PATH,
        }
        tornado.web.Application.__init__(self, handlers, **settings)


class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.render("index.html")


def main():
    applicaton = Application()
    http_server = tornado.httpserver.HTTPServer(applicaton)
    http_server.listen(9999)

    tornado.ioloop.IOLoop.instance().start()

if __name__ == "__main__":
    main()

And Settings.py

import os
dirname = os.path.dirname(__file__)

STATIC_PATH = os.path.join(dirname, 'static')
TEMPLATE_PATH = os.path.join(dirname, 'templates')

Use this code instead

class IndexDotHTMLAwareStaticFileHandler(tornado.web.StaticFileHandler):
    def parse_url_path(self, url_path):
        if not url_path or url_path.endswith('/'):
            url_path += 'index.html'

        return super(IndexDotHTMLAwareStaticFileHandler, self).parse_url_path(url_path)

now use that class instead of vanilla StaticFileHandler in your Application... job's done!

There is no need to explicitly add a StaticFileHandler ; just specify the static_path and it will serve those pages.

You are correct that you need a MainHandler, as for some reason Tornado will not serve the index.html file, even if you append the filename to the URL.

In that case, this slight modification to your code should work for you:

import os
import tornado.ioloop
import tornado.web
from tornado import web

__author__ = 'gvincent'

root = os.path.dirname(__file__)
port = 9999

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.render("index.html")

application = tornado.web.Application([
    (r"/", MainHandler),
    ], template_path=root,
    static_path=root)

if __name__ == '__main__':
    application.listen(port)
    tornado.ioloop.IOLoop.instance().start()

I have been trying this. Don't use render it has additional overhead of parsing templates and gives error on template type strings in static html. I found this is the simplest way. Tornado is looking for a capturing parenthesis in regex , just give it an empty capturing group.

import os
import tornado.ioloop
import tornado.web

root = os.path.dirname(__file__)
port = 9999

application = tornado.web.Application([
    (r"/()", tornado.web.StaticFileHandler, {"path": root, "default_filename": "index.html"})
])

This has effect of resolving / to index.html and also avoid unwanted resolves like /views.html to static_dir/views.html

This worked for me From the tornado docs :

To serve a file like index.html automatically when a directory is requested, set static_handler_args=dict(default_filename="index.html") in your application settings, or add default_filename as an initializer argument for your StaticFileHandler .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM