Tornado 也是最好用的輕量級 web server

跟 Flask 相比, tornado 似乎討論的文章比較少, 但Max最喜歡使用的 web server 是 tornado, 今天來介紹 tornado 如何與 Flask 一樣, 掛載靜態檔案.

Flash 的使用方法, 真的很直覺, 程式碼也超短:
https://stackoverflow.max-everyday.com/2023/02/flask-web-server-static-file/

Tornado 對靜態檔案的說明:
https://www.tornadoweb.org/en/stable/web.html#tornado.web.StaticFileHandler

使用下面的範例, 就可以存取到靜態檔案:

import asyncio
import tornado
from tornado.web import Application
from tornado.web import StaticFileHandler

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("Hello, world")

def make_app():
    return Application([
        (r"/", MainHandler),
        (r"/content/(.*)", StaticFileHandler, {"path": "./content"}),
    ])

async def main():
    app = make_app()
    app.listen(8888)
    await asyncio.Event().wait()

if __name__ == "__main__":
    asyncio.run(main())

說明: ./content/ 輸出網頁的 /content/

使用的執行範例:
http://127.0.0.1:8888/content/static.py

有一個奇怪的坑是, 如果你在 create Application(routes, **app_settings) object 時, 所帶入的 app_settings = dict( ) 中間使用 static_path, 例如:

app_settings = dict(
    template_path=os.path.join(os.path.dirname(__file__), "templates"),
    static_path=os.path.join(os.path.dirname(__file__), "static"),

這時候, 又在 routes 裡設了 static, 例如:

handlers = [
        (r'/', MainHandler),
        (r'/static/(.*)', StaticFileHandler, {'path': "static"}),
    ]

2邊就會衝突, 原本應該正常顯示的靜態檔案都會成錯誤訊息:

Traceback (most recent call last):
  File "D:\Programs\Python\Python39-32\lib\site-packages\tornado\web.py", line 1713, in _execute
    result = await result
  File "D:\Programs\Python\Python39-32\lib\site-packages\tornado\web.py", line 2611, in get
    self.absolute_path = self.validate_absolute_path(self.root, absolute_path)
  File "D:\Programs\Python\Python39-32\lib\site-packages\tornado\web.py", line 2813, in validate_absolute_path
    raise HTTPError(404)
tornado.web.HTTPError: HTTP 404: Not Found

根目錄以下到 /static/目錄下取檔案, 並針對 404 不存在的檔案做客製化:

class MyStaticFileHandler(StaticFileHandler):
    def write_error(self, status_code, *args, **kwargs):
        my_404_page = os.path.join(options.app_root, 'template/error_404.html')
        self.set_status(status_code)
        if status_code in [404]:
            self.render(my_404_page)
        else:
            self.write("Error: %d" % status_code)

handlers = [
    (r'/', 'app.controller.HomeHandler'),
    (r'/(.*)', MyStaticFileHandler, {'path': os.path.join(app_root, 'static/')}),
]

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *