Tornado框架01-入門總概

貓xian森發表於2017-03-16

我們首先來談談web框架. web框架的本質其實就是socket服務端再加上業務邏輯處理, 比如像是Tornado這樣的框架. 有一些框架則只包含業務邏輯處理, 例如Django, bottle, flask這些框架, 它們的使用需要依賴包含socket的第三方模組(即 wsgiref)來執行
在python中常見的web框架構建模式有以下兩種:

  • MVC框架:
    • 資料庫相關操作的Models
    • 檢視檔案的Views
    • 業務邏輯的Controllers
  • MTV框架:
    • 資料庫相關操作的Models
    • 模板檔案Templates
    • 業務邏輯的Views

以上兩種只是命名不同, 所遵循的的思想也只是大同小異

在使用Tornado框架前, 我們先使用wsgiref再加上自己寫的業務邏輯自定義一個web框架

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from wsgiref.simple_server import make_server


def index():
    return "This is index "


def news():
    return "welcome to news "


URLS = {
    '/index': index,
    '/news': news,
}


def RunServer(rq, rp):
    rp('200 OK', [('Content-Type', 'text/html')])
    url = rq['PATH_INFO']
    if url in URLS.keys():
        ret = URLS[url]()
    else:
        ret = '404'
    return ret


if __name__ == '__main__':
    http = make_server('', 8000, RunServer)
    http.serve_forever()複製程式碼
  • wsgiref在py2中執行正常, 在py3中會報錯
  • http = make_server('', 8000, RunServer) 這裡建立socket服務端, 並傳入業務邏輯功能函式RunServer(rq, rp)
  • http.serve_forever() 啟動服務端, 阻塞程式等待客戶端訪問, 一旦有訪問則執行RunServer(rq, rp)方法
  • RunServer(rq, rp) 該方法中rq封裝了請求資訊, rp封裝了響應資訊
    • url = rq['PATH_INFO'] 獲取請求的url連線地址
    • ret = URLS[url]() 根據請求的url執行對應的函式
  • 當我們將執行的index()news()功能函式放進Controllers業務邏輯處理模組, 將返回結果ret改為檔案讀寫後的內容, 並將該檔案放置到Views或者Template模組中, 就形成了最基礎版本的MVCMTV框架

接下來我們使用Tornado實現一個簡陋的任務表功能demo

Tornado框架01-入門總概
目錄結構

commons.css檔案內容:

.body {
    margin: 0;
    background-color: cornflowerblue;
}複製程式碼

index.html檔案內容:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>S1</title>
    <link rel="stylesheet" href="../static/commons.css">
</head>
<body>
<form method="post">
    <input type="text" name="name">
    <input type="submit" value="提交">
</form>
<h1>內容展示</h1>
<ul>
    {% for item in contents %}
        <li>{{item}}</li>
    {% end %}
</ul>
</body>
</html>複製程式碼

index.py檔案內容:

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import tornado.web, tornado.ioloop


class MyHandle(tornado.web.RequestHandler):
    def get(self, *args, **kwargs):
        self.render("index.html", contents=CONTENTS_LIST)

    def post(self, *args, **kwargs):
        CONTENTS_LIST.append(self.get_argument('name'))
        self.render('index.html', contents=CONTENTS_LIST)


if __name__ == '__main__':
    CONTENTS_LIST = []
    settings = {
        'template_path': 'template',
        'static_path': 'static',
        'static_url_prefix': 'static/',
    }

    application = tornado.web.Application([
        (r"/index", MyHandle)
    ], **settings)
    application.listen(80)
    tornado.ioloop.IOLoop.instance().start()複製程式碼

Tornado框架01-入門總概
客戶端第一次訪問

Tornado框架01-入門總概
第一次輸入提交

  • CONTENTS_LIST = [] 為存放的是輸入框輸入的內容
  • settings字典表示的是配置檔案
    • 'template_path': 'template' 模板檔案的存放位置
    • 'static_path': 'static' 靜態檔案的存放位置, 靜態檔案必須宣告, 否則瀏覽器無法找到靜態檔案
    • 'static_url_prefix': 'static/' 靜態檔案字首, 減少每個檔案引入都要加字首的麻煩
  • application = tornado.web.Application([
         (r"/index", MyHandle)
     ], **settings)複製程式碼
    根據瀏覽器的url確定其對應的處理類並生成該類的物件
  • application.listen(80) 設定服務端的監聽埠
  • tornado.ioloop.IOLoop.instance().start() 阻塞服務端程式, 等待客戶端的訪問
  • 客戶端第一次訪問呼叫的是MyHandle類中的get(self, *args, **kwargs)方法, 服務端向客戶端返回index.html檔案
  • 客戶端瀏覽器接受到index.html檔案之後, 在輸入框中輸入內容並提交之後會呼叫post(self, *args, **kwargs), 並將輸入的內容追加到
  • self.get_argument('name') 獲取指定引數的內容
    CONTENTS_LIST中, 服務端返回index.html, 返回過程中Toranado
    會將CONTENTS_LIST 的內容渲染到index.html之後才會發給客戶端瀏覽器

python中的模板引擎本質上是將html檔案轉換成一段python函式字串, 再通過compileexec將該函式執行, 以此來進行模板渲染

現在我們介紹一下模板引擎的使用:

Tornado框架01-入門總概
專案目錄

uimethod.py檔案如下:

#!/usr/bin/env python
# -*- coding:utf-8 -*-

def test_uimethod(self):
    return "uimethod"複製程式碼

uimodule.py檔案如下:

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from tornado.web import UIModule

class MyClass(UIModule):
    def render(self, *args, **kwargs):
        return "uimodule"複製程式碼

index.py檔案如下:

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import tornado.web, tornado.ioloop
import uimethod as ut
import uimodule as ud


class MyHandle(tornado.web.RequestHandler):
    def get(self, *args, **kwargs):
        self.render("index.html", ag="this is ag", contents=CONTENTS_LIST)

    def post(self, *args, **kwargs):
        CONTENTS_LIST.append(self.get_argument('name'))
        self.render('index.html', contents=CONTENTS_LIST)


if __name__ == '__main__':
    CONTENTS_LIST = []
    settings = {
        'template_path': 'template',
        'static_path': 'static',
        'static_url_prefix': 'static/',
        'ui_methods': ut,
        'ui_modules': ud
    }

    application = tornado.web.Application([
        (r"/index", MyHandle)
    ], **settings)
    application.listen(80)
    tornado.ioloop.IOLoop.instance().start()複製程式碼

index.html檔案如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>S1</title>
    <link rel="stylesheet" href='{{static_url("commons.css")}}'>
</head>
<body>
<h1>{{ag}}</h1>
<h1>{{test_uimethod()}}</h1>
<h1>{%module MyClass()%}</h1>
<form method="post">
    <input type="text" name="name">
    <input type="submit" value="提交">
</form>
<h1>內容展示</h1>
<ul>
    {% for item in contents %}
    <li>{{item}}</li>
    {% end %}
</ul>
<hr>
</body>
</html>複製程式碼

我們看看客戶端訪問的結果:

Tornado框架01-入門總概
訪問結果

  • 模板引擎中的{{key}}表示取key對應的值, 當key為函式時候執行該函式並取該函式結果. 例如index.html檔案中的<h1>{{ag}}</h1> 實際上取得是index.pyself.render("index.html", ag="this is ag", contents=CONTENTS_LIST)中的引數ag的值
  • <h1>{{test_uimethod()}}</h1> 這裡執行的是自定義函式, 我們將這個自定義函式寫在uimethod.py檔案中, 並且在index.py檔案中匯入, 然後將index.py檔案中的settings配置增加一行'ui_methods': ut, 該行內容表示模板引擎可執行自定義函式
  • 模板引擎中的{%%}可用於迴圈語句和條件語言以及自定義類的執行, {% for item in contents %}此處正是用於迴圈遍歷contents中的內容
  • <h1>{%module MyClass()%}</h1>此處表示模板引擎執行自定義類, 該類的檔案對應的是uimodule.py檔案, 我們需要在index.pysettings中增加一行'ui_modules': ud, 改行表示模板引擎可使用自定義類
  • 注意, 我們將index.html檔案引入css的方式改為了<link rel="stylesheet" href='{{static_url("commons.css")}}'>, static_url()是模板引擎內建的自定義函式, 用該函式引入css檔案時候, 僅當css檔案內容發生變化時候, 瀏覽器才會重新快取該css檔案

考慮到篇幅太長不容易閱讀, 筆者這裡將關於Tornado框架的cookie知識, 自定義session的使用 路由系統,以及模板引擎高階部分放在後期文章分篇共享

相關文章