溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Python中如何使用Tornado

發(fā)布時間:2021-10-18 15:41:12 來源:億速云 閱讀:141 作者:小新 欄目:編程語言

這篇文章給大家分享的是有關Python中如何使用Tornado的內容。小編覺得挺實用的,因此分享給大家做個參考,一起跟隨小編過來看看吧。

Tornado 基本操作

Hello World

經典的 hello world 示例:

import tornado.web

# 視圖
class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("Hello World.")

# 路由
application = tornado.web.Application([
    (r"/", MainHandler),
    (r"/hello", MainHandler),
])

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

整個過程其實就是在創(chuàng)建一個socket服務端并監(jiān)聽8000端口。當請求到來時,根據請求中的url和請求方式(post、get或put等)來指定相應的類中的方法來處理本次請求。在上述示例中 url 在路由系統(tǒng)匹配到時,則服務器會給瀏覽器返回 Hello World ,否則返回 404: Not Found(tornado內部定義的值), 即完成一次http請求和響應。

模板引擎

Tornao中的模板語言和django中類似。模板引擎將模板文件載入內存,然后將數據嵌入其中,最終獲取到一個完整的字符串,再將字符串返回給請求者。
不過還是有區(qū)別的。Tornado 的模板支持“控制語句”和“表達語句”,控制語句是使用 {% 和 %} 包起來的。例如 {% if len(items) > 2 %}。表達語句是使用 {{ 和 }} 包起來的,例如 {{ items[0] }} 。
控制語句和對應的 Python 語句的格式基本完全相同。支持 if、for、while 和 try,這些語句邏輯結束的位置需要用 {% end %} 做標記。還通過 extends 和 block 語句實現(xiàn)了模板繼承。這些在 template 模塊的代碼文檔中有著詳細的描述。在使用模板前需要在setting中設置模板路徑:"template_path" : "tpl"
使用模板引擎的簡單示例,后端代碼:

import tornado.web

class IndexHandler(tornado.web.RequestHandler):
    def get(self):
        self.render("index.html", k1='v1', k2='v2')  # k1和k2是傳給模板引擎處理的內容

application = tornado.web.Application([
    (r"/index", IndexHandler),
])

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

前端代碼,模板語言的使用:

<body>
<h2>Hello World</h2>
<h4>{{ k1 }}</h4>
{% if k2 == 'v2' %}
    <h4>k2 == v2</h4>
{% else %}}
    <h3>k2 != v2</h3>
{% end %}
</body>

加載配置

上面的前端代碼,最好是統(tǒng)一保存在某個目錄里,比如新建個tpl目錄來存放。把html文件移過去之后,現(xiàn)在render()方法就找不到這個文件了。當然可以改一下參數,把目錄名加進去。不過推薦的做法是把tpl目錄加到配置里去,對上面的代碼進去修改,加入配置信息:

class IndexHandler(tornado.web.RequestHandler):
    def get(self):
        self.render("index.html", k1='v1', k2='v2')

# 配置就是個key-value的字段
settings = {
    'template_path': 'tpl'
}

application = tornado.web.Application([
    (r"/index", IndexHandler),
], **settings)  # application加載配置信息

POST

先準備好如下的頁面,在輸入框里填入要搜索的關鍵字,提交后就跳轉到搜索引擎搜索的結果:

<body>
<form method="POST" action="/baidu">
    <input type="text" name="wd" />
    <input type="submit" value="百度一下" />
</form>
</body>

后端的代碼:

import tornado.web

class SearchHandler(tornado.web.RequestHandler):
    def get(self):
        self.render("baidu.html")

    def post(self):
        wd = self.get_argument('wd')
        print(wd)
        self.redirect('https://www.baidu.com/s?wd=%s' % wd)

settings = {
    'template_path': 'tpl'
}

application = tornado.web.Application([
    (r"/baidu", SearchHandler),
], **settings)

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

上面的示例,post請求最后是用redirect()返回的,這個是頁面的跳轉。
獲取提交的參數的方法有這些:

class LoginHandler(tornado.web.RequestHandler):
    def post(self):
        # 獲取URL中以GET形式傳遞的數據
        self.get_query_argument()
        self.get_query_arguments()
        # 獲取請求體中以POST形式傳遞的數據
        self.get_body_argument()
        self.get_body_arguments()
        # 從上面2個里都嘗試獲取
        self.get_argument()
        self.get_arguments()

靜態(tài)文件(圖片)

靜態(tài)文件是給用戶直接下載的,所以應該單獨存放,并且在配置里注冊對應的目錄。配置的寫法:

settings = {
    'template_path': 'tpl',  # 模板
    'static_path': 'imgs',  # 靜態(tài)文件
}

現(xiàn)在可以根據配置里的名稱去創(chuàng)建一個新的文件夾 static 用來存放靜態(tài)文件。然后放張圖片進去。
這里故意不用static作為靜態(tài)文件文件夾的名稱,這里只是注冊文件夾,但是前端引用的時候,無論你的靜態(tài)文件放在那里,都是用 static/文件名稱 。
加一個img標簽到html里,然后驗證一下效果。注意src里用的是static,而不是文件夾真正的名稱:

<img src="static/test.jpg" />

這里前端引用的是必須用static,不過這個名字也是可以自定義的:

settings = {
    'template_path': 'tpl',  # 模板
    'static_path': 'imgs',  # 靜態(tài)文件
    'static_url_prefix': '/statics/',  # 注意兩邊都要有斜杠/
}

其他操作

self.request.cookies : 獲取cookies
self.set_cookie() : 設置cookie
self.request.headers : 獲取請求頭
self.set_header() : 設置響應頭,如果出現(xiàn)同一個響應頭,會覆蓋
self.add_header() : 設置響應頭,如果出現(xiàn)同一個響應頭,則追加

Tornado 沒有提供 session ,所以要用的話,得另外寫。同樣的,緩存也沒有。

進階操作

最基本的就是上面那些了,這里再補充一點別的。

自定義UIMethod以及UIModule

這個就是模板引擎里的自定義函數。
UIMethod 自定義的是個函數,UIModule 自定義的是個類。

定義
把自定義的函數和自定義的類單獨寫在文件里:

# ui_methods.py
def test1(self):  # 這里的self不能去掉
    return "TEST1"

def test2(self):
    return "TEST2"

# ui_module.py
from tornado.web import UIModule
from tornado import escape

class Test(UIModule):
    def render(self, *args, **kwargs):
        return escape.xhtml_escape('<h4>UI Module TEST</h4>')

注冊
寫一個完整的服務,這里加上注冊的代碼。先導入上面的文件,然后分別在settings里注冊:

import tornado.web
import ui_methods as mt
import ui_modules as md

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.render('ui.html')

settings = {
    'template_path': 'tpl',  # 模板
    'static_path': 'static',  # 靜態(tài)文件,這里不重要
    'static_url_prefix': '/statics/',  # 注意兩邊都要有斜杠/
    'ui_methods': mt,
    'ui_modules': md,
}

application = tornado.web.Application([
    (r"/ui", MainHandler),
], **settings)

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

使用
這里只需要看明白前端調用的方法就可以了

<body>
<h2>UI Method</h2>
<p>{{ test1() }}</p>
<p>{{ test2() }}</p>
<h2>UI Module</h2>
{% module Test() %}
</body>

UIModule里的方法
render 方法返回的內容就是調用模板的位置顯示的內容:

class Test(UIModule):

    def javascript_files(self):
        pass

    def embedded_javascript(self):
        pass

    def css_files(self):
        pass

    def embedded_css(self):
        pass

    def render(self, *args, **kwargs):
        return escape.xhtml_escape('<h4>UI Module TEST</h4>')

javascript的方法會在body的尾部插入script標簽,插入js代碼
css的方法則會在head里插入style標簽,設置css
files就是直接引入文件,進行設置
embedded就是插入返回的字符串作為設置

CSRF

首先在settings里啟用csrf:

settings = {
    "xsrf_cookies": True,
}

在 form 中使用

<form action="/new_message" method="post">
  {% raw xsrf_form_html() %}
  <input type="text" name="message"/>
  <input type="submit" value="提交"/>
</form>

{{ xsrf_form_html() }} 能夠輸出完整的input標簽,但是直接用回被解析為字符串,帶著標簽的信息作為字符串顯示出來。所以上面用的是 {% raw xsrf_form_html() %} 。

在 Ajax 中使用
Ajax使用時,本質上就是去獲取本地的cookie,攜帶cookie再來發(fā)送請求:

function getCookie(name) {
    var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");
    return r ? r[1] : undefined;
}

jQuery.postJSON = function(url, args, callback) {
    args._xsrf = getCookie("_xsrf");
    $.ajax({url: url, data: $.param(args), dataType: "text", type: "POST",
        success: function(response) {
        callback(eval("(" + response + ")"));
    }});
};

上傳文件

先準備一個form表單上傳文件的html頁面:

<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <title>上傳文件</title>
</head>
<body>
    <form id="my_form" name="form" action="/index" method="POST"  enctype="multipart/form-data" >
        <input name="fff" id="my_file"  type="file" />
        <input type="submit" value="提交"  />
    </form>
</body>
</html>

接收上傳文件:

import tornado.web

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

    def post(self, *args, **kwargs):
        file_metas = self.request.files["fff"]
        # print(file_metas)
        for meta in file_metas:
            file_name = meta['filename']
            with open(file_name,'wb') as up:
                up.write(meta['body'])

settings = {
    'template_path': 'template',
}

application = tornado.web.Application([
    (r"/index", MainHandler),
], **settings)

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

上傳文件還可以用Ajax,另外還有借助iframe標簽實現(xiàn)的偽Ajax的實現(xiàn),略...

異步非阻塞

異步非阻塞IO,高并發(fā)高性能是tornado的特點,所以這小節(jié)很重要。但是具體內容也沒搞明白,只能盡量先記一些。
首先要引入下面的2個模塊:

from tornado import gen
from tornado.concurrent import Future

class AsyncHandler(tornado.web.RequestHandler):

    @gen.coroutine
    def get(self):
        future = Future()
        future.add_done_callback(self.doing)
        yield future

    def doing(self,*args, **kwargs):
        self.write('async')
        self.finish()

當發(fā)送GET請求時,由于方法被@gen.coroutine裝飾且yield 一個Future對象,那么Tornado會等待,等待用戶向future對象中放置數據或者發(fā)送信號,如果獲取到數據或信號之后,就開始執(zhí)行doing方法。
這里發(fā)送請求后,永遠也不會返回,就是按上面說的Tornado一直在等待。等待調用了 future.set_result(result) 這個方法。之后就會調用回調函數,而set_result方法里傳遞進去的參數,可以通過 future.result() 獲取到。大致就是這么的用法,但是沒有個使用示例有點不好理解。

Future類
Future類位于tornado源碼的concurrent模塊中。下面是Future類里的一部分代碼作為分析之用:

class Future(object):
    def done(self):
        return self._done

    def result(self, timeout=None):
        self._clear_tb_log()
        if self._result is not None:
            return self._result
        if self._exc_info is not None:
            raise_exc_info(self._exc_info)
        self._check_done()
        return self._result

    def add_done_callback(self, fn):
        if self._done:
            fn(self)
        else:
            self._callbacks.append(fn)

    def set_result(self, result):
        self._result = result
        self._set_done()

    def _set_done(self):
        self._done = True
        for cb in self._callbacks:
            try:
                cb(self)
            except Exception:
                app_log.exception('exception calling callback %r for %r',
                                  cb, self)
        self._callbacks = None

Future類重要成員函數:

  • def done(self) : Future的_result成員是否被設置

  • def result(self, timeout=None) : 獲取Future對象的結果

  • def add_done_callback(self, fn) : 添加一個回調函數fn給Future對象。如果這個Future對象已經done,則直接執(zhí)行fn,否則將fn加入到Future類的一個成員列表中保存。

  • def_set_done(self) : 一個內部函數,主要是遍歷列表,逐個調用列表中的callback函數,也就是前面 add_done_calback 加如來的。

  • def set_result(self, result) : 給Future對象設置result,并且調用_set_done。也就是說,當Future對象獲得result后,所有add_done_callback加入的回調函數就會執(zhí)行。

這里最終就是希望 future 調用 set_result ,然后就是執(zhí)行回調函數。

自定義異步非阻塞Web框架

這節(jié)主要是想以源碼的方式展示分析tornado是怎么實現(xiàn)異步非阻塞的。代碼應該不是超的源碼,只是借鑒了思路,做了很多簡化。
下面是實現(xiàn)異步非阻塞的代碼,主要是 select+socket :
https://www.cnblogs.com/wupeiqi/p/6536518.html

什么場景考慮使用Tornado

復雜的應用還是用django來開發(fā)。
如果要開發(fā)一個API的功能,或者其他的簡單的工具、應用,也不用操作數據庫。就可以用tornado或者是其他簡單的框架。就不需要用django了。
可以選擇的簡單的框架還有Flask。

感謝各位的閱讀!關于“Python中如何使用Tornado”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,讓大家可以學到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI