我們首先來談談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
模組中, 就形成了最基礎版本的MVC
和MTV
框架
接下來我們使用Tornado
實現一個簡陋的任務表功能demo
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()複製程式碼
CONTENTS_LIST = []
為存放的是輸入框輸入的內容settings
字典表示的是配置檔案'template_path': 'template'
模板檔案的存放位置'static_path': 'static'
靜態檔案的存放位置, 靜態檔案必須宣告, 否則瀏覽器無法找到靜態檔案'static_url_prefix': 'static/'
靜態檔案字首, 減少每個檔案引入都要加字首的麻煩
根據瀏覽器的url確定其對應的處理類並生成該類的物件application = tornado.web.Application([ (r"/index", MyHandle) ], **settings)複製程式碼
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
函式字串, 再通過compile
和exec
將該函式執行, 以此來進行模板渲染
現在我們介紹一下模板引擎的使用:
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>複製程式碼
我們看看客戶端訪問的結果:
- 模板引擎中的
{{key}}
表示取key
對應的值, 當key
為函式時候執行該函式並取該函式結果. 例如index.html
檔案中的<h1>{{ag}}</h1>
實際上取得是index.py
的self.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.py
的settings
中增加一行'ui_modules': ud
, 改行表示模板引擎可使用自定義類- 注意, 我們將
index.html
檔案引入css
的方式改為了<link rel="stylesheet" href='{{static_url("commons.css")}}'>
,static_url()
是模板引擎內建的自定義函式, 用該函式引入css
檔案時候, 僅當css
檔案內容發生變化時候, 瀏覽器才會重新快取該css
檔案
考慮到篇幅太長不容易閱讀, 筆者這裡將關於Tornado
框架的cookie
知識, 自定義session
的使用 路由系統,以及模板引擎高階部分放在後期文章分篇共享