Tornado

Torando基礎講解

  1. Torando的介紹
  2. Torando和Django的比較
  3. Torando入門程序
    1. HttpServer的介紹
    2. options的使用
  4. Torando快速開始
  5. 基礎工程
  6. Application的講解
    1. 配置(config.py)
    2. 路由系統
      1. 請求部分
      2. 響應部分
  7. 模板引擎
    1. 母版
    2. 自動轉義
    3. 導入
    4. 自定義UIMethod以UIModule
  8. 靜態文件
  9. Model的講解
    1. 防止SQL注入
    2. 使用pymysql操做
  10. cookie的設置
    1. cookie的應用
  11. XSRF
  12. 同步阻塞
  13. 異步非阻塞

Torando的介紹

Tornado 是一個Python web框架和異步網絡庫, 經過使用非阻塞網絡I/OTornado能夠支撐上萬級的鏈接,處理 長鏈接, WebSockets ,和其餘須要與每一個用戶保持長久鏈接的應用.css

tornado介紹.png

說明:html

Tornado 應該運行在類 Unix 的平臺上, 在線上部署的時候,爲了最佳的性能和擴展性,僅推薦 Linux 和 BSD平臺,由於須要充分利用linux的epoll和BSD的Kqueue,這個也是Tornado不依靠多進程/多線程達到高性能的緣由python

 

Torando和Django的比較
特性 Django Torando
路由系統
視圖函數
模板引擎
ORM操做
cookie
session
緩存,信號,Form,Admin
Torando入門程序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
 
'''
tornado 的基礎 web 框架模塊
'''
import tornado.web
import tornado.options
 
'''
tornado 的核心 IO 循環模塊,封裝了 linux 的 epoll 和 BSD 的 Kqueue, 是 Tornado 高效的基礎
'''
import tornado.ioloop
 
 
'''
類比 Django 中的 CBV 模式
一個業務處理的邏輯類
'''
class IndexHandler(tornado.web.RequestHandler):
 
'''
處理 get 請求的, 不能處理 post 請求
'''
def get(self):
print( 'hello world')
'''
將數據返回給 web 的頁面,相似於 HttpResponse
'''
self.write( 'this is web page!')
 
def post(self, *args, **kwargs):
print(*args, **kwargs)
 
 
class StoryHandler(tornado.web.RequestHandler):
 
def get(self, id, *args, **kwargs):
print(id, args, kwargs)
self.write(id)
 
 
if __name__ == '__main__':
'''
實例化一個對象
Application 是 tornado.web 的一個核心類
裏面保存了路由映射表,有一個 listen 方法, 建立了一個 socket 服務器,並綁定了一個端口 8001
'''
app = tornado.web.Application([
( r'/', IndexHandler),
( r'/story/([a-zA-Z0-9]+)/', StoryHandler)
])
 
## 綁定監聽端口, 此時咱們的服務器並無開始監聽
app.listen( 8001)
 
'''
IOLoop.current:返回當前線程的 IOLoop 實例
IOLoop.start: 啓動 IOLoop 實例的 I/O 循環,同時開啓了監聽
'''
tornado.ioloop.IOLoop.current().start()

執行過程:mysql

  • 第一步:執行腳本,監聽 8888 端口
  • 第二步:瀏覽器客戶端訪問 / --> http://127.0.0.1:8888/
  • 第三步:服務器接受請求,並交由對應的類處理該請求
  • 第四步:類接受到請求以後,根據請求方式(post/get/delete...)的不一樣調用並執行相應的方法
  • 第五步:方法返回值的字符串內容發送瀏覽器

tornado的高性能原理.jpg

HttpServer的介紹
1
2
3
4
5
6
7
8
 
import tornado.httpserver
 
# 實例化一個httpserver的實例
httpServer = tornado.httpserver.HTTPServer(app)
 
# 綁定端口
httpServer.listen( 8000)

但上述 tornado 默認啓動的是一個進程jquery

如何啓動多個進程?linux

1
2
3
4
5
6
7
8
9
10
 
import tornado.httpserver
 
# 實例化一個httpserver的實例
httpServer = tornado.httpserver.HTTPServer(app)
 
# 綁定端口
<!-- httpServer.listen(8000) -->
httpServer.bind(8000) # 將服務器綁定到指定到端口
httpServer.start(5) # num默認開啓一個進程, 若是值大於0,建立對應個數的進程

可是上面存在三個問題:web

  • 全部的子進程都是由一個命令啓動的,沒法作到不中止服務的狀況下修改代碼。若是我想修改某一個進程中的代碼,就必須得中止全部的
  • 全部的進程都共享一個端口號,想要分別監控很是困難

因爲上面的問題, 咱們不建議使用上面的方式開啓多個進程, 所以咱們之後建議使用 app.listen(8000)sql

options的使用

tornado爲咱們提供了一個便捷的 tornado.options 模塊數據庫

基礎方法與屬性:django

tornado.options.define() : 用來定義變量的方法

1
2
3
4
5
6
7
8
9
參數:
name : 變量名, 必須保證其惟一性, 否則會報錯
default: 默認值
type : 設置變量的類型, 傳入值的時候, 會根據類型進行轉換, 轉換失敗回報錯, 能夠是 str, int , float, 若是沒有設置,則會根據default的值進行轉換, 但若是default沒有設置, 那麼則不會進行轉換
help : 提示信息
 
### 使用示例:
tornado.options.define( 'port', default = 8000)
tornado.options.define( 'list', default = [], type=str, mutiple= True)

tornado.options.options : 全局的 options 對象, 全部定義的變量, 均可以做爲該對象的屬性

1
tornado.options.options.port

tornado.options.parse_command_line() : 轉換命令行參數, 並將轉換的參數值保存在 options對象中

1
python server.py --port= 9000 --list=good,stephen,lxx

tornado.options.parse_config_file(path) : 從配置文件導入參數

1
2
加載在同一級的config文件
tornado.options.parse_config_file( "config")

缺點:

  • 上述的配置文件方式要求咱們必須使用 Python 的語法格式去寫
  • 調用參數的時候,不支持字典類型

所以使用下面的方式:

建立一個 config.py 的文件

1
2
3
4
5
6
7
8
9
 
options = {
"port" : 8080,
"names" : [ "stephen", 'lxxx', 'xxx']
}
 
import config
 
print( "list:", config.options.port)
Torando快速開始

由一個簡單的Demo來講,MVC的結構框架

MVC的形象解釋 MVC的原理

基礎工程

重新整理一個基礎工程項目代碼:

1
2
3
4
5
6
7
8
9
├── application.py : 管理路由映射關係文件
├── config.py : 全部的配置文件
├── models : 數據庫文件夾
├── server.py : 啓動服務文件
├── static : 項目靜態目錄文件
├── template : 項目模板文件
└── views : 項目的視圖處理文件夾
├── __init__.py
└── index.py

各個文件的代碼:

application.py 文件:

點擊顯/隱
1
2
3
4
5
6
7
8
9
10
11
12
13
#author:shangzekai
 
import tornado.web
from views import index
import config
 
class Application(tornado.web.Application):
 
def __init__(self):
path = [
( r'/', index.IndexHandler),
]
super(Application, self).__init__(path, **config.settings)

server.py 文件:

{% fold %}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 
import config
 
import tornado.ioloop
 
from application import Application
 
 
if __name__ == '__main__':
app = Application()
app.listen(config.options[ 'port'])
'''
IOLoop.current:返回當前線程的 IOLoop 實例
IOLoop.start: 啓動 IOLoop 實例的 I/O 循環,同時開啓了監聽
'''
tornado.ioloop.IOLoop.current().start()
{% endfold %}

config.py 文件:

點擊顯/隱
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import os
BASE_DIR = os.path.dirname(__file__)
 
options = {
'port': 8010
}
 
mysql = {
'dbhost': 'localhost',
'dbuser': 'root',
'dbpwd': '123qwe',
'dbname': 'test',
'dbcharset': 'utf8'
}
 
settings = {
'debug' : True,
'static_path' : os.path.join(BASE_DIR, 'static'),
'template_path' : os.path.join(BASE_DIR, 'template'),
'xsrf_cookies' : True,
'login_url' : 'login'
}
Application的講解
配置(config.py)
1
2
'static_path' : os.path.join(BASE_DIR, 'static'),
'template_path' : os.path.join(BASE_DIR, 'template'),

debug: 設置tornado是否工做在調試模式下,默認爲False即工做在生產模式下

若是debug設置爲 True , 則會可有以下效果

  • 自動重啓
1
2
3
1. tornado應用會監控源代碼文件,當有代碼改動的時候便會重啓服務器, 減小手動重啓服務器的次數
2. 若是保存後代碼有錯誤會致使重啓失敗,修改錯誤後須要手動重啓
3. 也能夠經過 autoreload = True 來單獨設置自動重啓
  • 取消緩存編譯的模板, 開發階段須要關閉緩存
  • 取消緩存靜態文件
  • 提供錯誤追蹤信息
路由系統

路由系統其實就是 url 和 類 的對應關係,這裏不一樣於其餘框架,其餘不少框架均是 url 對應 函數,Tornado中每一個url對應的是一個類。

請求部分
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import tornado.ioloop
import tornado.web
 
 
class MainHandler(tornado.web.RequestHandler):
def initialize(self, name, age):
self.name = name
self.age = age
 
def get(self):
self.write( "Hello, world")
 
class StoryHandler(tornado.web.RequestHandler):
def get(self, story_id):
self.write( "You requested the story " + story_id)
 
application = tornado.web.Application([
( r"/index", MainHandler),
( r"/story/([0-9]+)", StoryHandler, { "name": 'zhangsan', 'age': 12}),
])
 
 
if __name__ == "__main__":
application.listen( 80)
tornado.ioloop.IOLoop.instance().start()

反向路由解析

1
tornado.web.url( r'/xxx', index.xxHandler, name= 'xxx')

requestHandler

  • 獲取get請求的參數值:
1
2
self.get_query_argument(name, default, strip= True) : 返回值
self.get_query_arguments(name, strip= True) : 返回一個列表
  • 獲取post請求的參數值:
1
2
self.get_body_argument(name, default, strip= True)
self.get_body_arguments(name, strip= True)
  • 既獲取get的又獲取post的參數:
1
2
self.get_argument(name, default, strip= True)
self.get_arguments(name, strip= True)

request對象

存儲了請求相關的信息

  • method : HTTP 請求的方式
  • host : 請求的主機名
  • query : 請求的參數部分
  • version: 請求的Http版本
  • headers: 請求的頭信息, 字典類型
  • remote_ip : 客戶端IP
  • files : 用戶上傳的文件

tornado.httputil.HTTPFile 對象

接收到的文件對象

  • filename : 文件的實際名字
  • body : 文件的數據實體
  • content_type : 文件的類型
1
2
3
4
5
6
7
8
9
10
 
def post():
filesDict = self.request.files
 
for inputname in filesDict:
filesArr = filesDict[inputname]
for fileObj in filesArr:
filePath = os.path.join(config.BASE_DIR, 'upfile/'+fileObj.filename)
with open(filepath, 'wb') as fp:
fp.write(fileObj.body)
響應部分
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
 
self.write() : 刷新緩存區
 
例子:
info = {
"name" : 'zhansgan',
"age" : 16
}
 
直接以json的格式返回
self.write(info)
使用json.dumps(info)變成字符串
self.write(json.dumps(info))
 
注意:
本身手動序列化的,content-type的屬性是text/html的, 而自動序列化的, content-type的屬性是application/json的
 
 
 
self.finish() : 也是刷新緩存區, 可是會關閉當次請求通道, 在finish下面就不能再write了, 沒有意義了
 
self.set_header(name, value): 手動設置一個名爲name, 值爲value的響應頭字段
 
self.set_status(status_code, reason): 設置響應狀態嗎
 
self.redirect(url) : 重定向url
模板引擎

Tornao中的模板語言和django中相似,模板引擎將模板文件載入內存,而後將數據嵌入其中,最終獲取到一個完整的字符串,再將字符串返回給請求者

Tornado 的模板支持「控制語句」和「表達語句」,控制語句是使用 {%` 和 `%} 包起來的 例如 {% if len(items) > 2 %}。表達語句是使用 {{` 和 `}} 包起來的,例如 {{ items[0] }}

控制語句和對應的 Python 語句的格式基本徹底相同。咱們支持 if、for、while 和 try,這些語句邏輯結束的位置須要用 {% end %} 作標記。還經過 extendsblock 語句實現了模板繼承。這些在 template 模塊 的代碼文檔中有着詳細的描述。

注:在使用模板前須要在setting中設置模板路徑:"template_path" : "tpl"

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<title>XXX</title>
<link href=" {{static_url("css/common.css")}}" rel="stylesheet" />
</head>
<body>
<div>
<ul>
{% for item in list_info %}
<li> {{item}}</li>
{% end %}
</ul>
</div>
<script src=" {{static_url("js/jquery-1.8.2.min.js")}}"></script>
</body>
</html>

在模板中默認提供了一些函數、字段、類以供模板使用

1
2
3
4
5
6
7
8
9
10
11
escape: tornado.escape.xhtml_escape 的別名
xhtml_escape: tornado.escape.xhtml_escape 的別名
url_escape: tornado.escape.url_escape 的別名
json_encode: tornado.escape.json_encode 的別名
squeeze: tornado.escape.squeeze 的別名
linkify: tornado.escape.linkify 的別名
datetime: Python 的 datetime 模組
request: handler.request 的別名
current_user: handler.current_user 的別名
static_url: for handler.static_url 的別名
xsrf_form_html: handler.xsrf_form_html 的別名
母版
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<title>XXX</title>
<link href=" {{static_url("css/common.css")}}" rel="stylesheet" />
{% block CSS %}{% end %}
</head>
<body>
 
<div class="pg-header">
 
</div>
 
{% block RenderBody %}{% end %}
 
<script src=" {{static_url("js/jquery-1.8.2.min.js")}}"></script>
 
{% block JavaScript %}{% end %}
</body>
</html>

index.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{% extends 'layout.html'%}
{% block CSS %}
<link href=" {{static_url("css/index.css")}}" rel="stylesheet" />
{% end %}
 
{% block RenderBody %}
<h1>Index</h1>
 
<ul>
{% for item in li %}
<li> {{item}}</li>
{% end %}
</ul>
 
{% end %}
 
{% block JavaScript %}
 
{% end %}

 

自動轉義

tornado 是默認開啓轉義功能的

若是想使用原生的代碼展現,使用以下的代碼:

1
{{ raw xxx }}

在配置中添加:

1
autoescape : None ## 關閉全部的轉義功能

escape()函數 : 在關閉自動轉義以後,對特定的變量進行轉義

導入
1
2
3
4
5
6
<div>
<ul>
<li>1024</li>
<li>42區</li>
</ul>
</div>

index.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<title>XXX</title>
<link href=" {{static_url("css/common.css")}}" rel="stylesheet" />
</head>
<body>
 
<div class="pg-header">
{% include 'header.html' %}
</div>
 
<script src=" {{static_url("js/jquery-1.8.2.min.js")}}"></script>
 
</body>
</html>

 

自定義UIMethod以UIModule

定義

1
2
3
4
# uimethods.py
 
def tab(self):
return 'UIMethod'
1
2
3
4
5
6
from tornado.web import UIModule
from tornado import escape
 
class custom(UIModule):
def render(self, *args, **kwargs):
return escape.xhtml_escape( '<h1>helloworld</h1>')

註冊

1
2
3
4
5
6
7
8
9
10
import uimodules as md
import uimethods as mt
 
settings = {
'template_path': 'template',
'static_path': 'static',
'static_url_prefix': '/static/',
'ui_methods': mt,
'ui_modules': md,
}

使用

1
2
3
4
5
6
7
8
9
10
11
12
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
<link href=" {{static_url("commons.css")}}" rel="stylesheet" />
</head>
<body>
<h1>hello</h1>
{% module custom(123) %}
{{ tab() }}
</body>
靜態文件

對於靜態文件,能夠配置靜態文件的目錄和前段使用時的前綴,而且Tornaodo還支持靜態文件緩存。

app.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import tornado.ioloop
import tornado.web
 
 
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.render( 'home/index.html')
 
settings = {
'template_path': 'template',
'static_path': 'static',
'static_url_prefix': '/static/',
}
 
application = tornado.web.Application([
( r"/index", MainHandler),
], **settings)
 
 
if __name__ == "__main__":
application.listen( 80)
tornado.ioloop.IOLoop.instance().start()

index.html

1
2
3
4
5
6
7
8
9
10
11
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
<link href=" {{static_url("commons.css")}}" rel="stylesheet" />
</head>
<body>
<h1>hello</h1>
</body>
</html>

靜態文件緩存實現

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def get_content_version(cls, abspath):
"""Returns a version string for the resource at the given path.
 
This class method may be overridden by subclasses. The
default implementation is a hash of the file's contents.
 
.. versionadded:: 3.1
"""
data = cls.get_content(abspath)
hasher = hashlib.md5()
if isinstance(data, bytes):
hasher.update(data)
else:
for chunk in data:
hasher.update(chunk)
return hasher.hexdigest()
Model的講解

Tornado3.0版本之前提供tornado.database模塊用來操做MySQL數據庫,而從3.0版本開始,此模塊就被獨立出來,做爲torndb包單獨提供。torndb只是對MySQLdb的簡單封裝,不支持Python 3

user.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import pymysql
 
class UserModel():
def __init__(self):
try:
self.db = pymysql.Connection(host= '127.0.0.1', database= 'test', user= 'root', password= '123qwe', charset= 'utf8',cursorclass=pymysql.cursors.DictCursor)
except Exception as e:
return print(e)
 
def getInfo(self):
try:
cursor = self.db.cursor()
temp = "select * from admin"
effect_row = cursor.execute(temp)
result = cursor.fetchall()
self.db.commit()
cursor.close()
self.db.close()
 
return result
except Exception as e:
return print(e)

 

防止SQL注入

搭建一個登錄的環境

app.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
 
import tornado.ioloop
import tornado.web
import pymysql
 
class LoginHandler(tornado.web.RequestHandler):
def get(self):
self.render( 'login.html')
 
def post(self, *args, **kwargs):
username = self.get_argument( 'username', None)
pwd = self.get_argument( 'pwd', None)
 
# 建立數據庫鏈接
conn = pymysql.connect(host= '127.0.0.1', port= 3306, user= 'root', passwd= '123456', db= 'shop')
cursor = conn.cursor()
 
# %s 要加上'' 不然會出現KeyboardInterrupt的錯誤
temp = "select name from userinfo where name='%s' and password='%s'" % (username, pwd)
effect_row = cursor.execute(temp)
result = cursor.fetchone()
conn.commit()
cursor.close()
conn.close()
 
if result:
self.write( '登陸成功!')
else:
self.write( '登陸失敗!')
 
 
settings = {
'template_path': 'template',
}
 
application = tornado.web.Application([
( r"/login", LoginHandler),
],**settings)
 
if __name__ == "__main__":
application.listen( 8000)
tornado.ioloop.IOLoop.instance().start()

在template文件夾下,放入login.html文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form method="post" action="/login">
<input type="text" name="username" placeholder="用戶名"/>
<input type="text" name="pwd" placeholder="密碼"/>
<input type="submit" value="提交" />
</form>
</body>
</html>

至此一個簡單的登錄系統就說完了,按照正常的方式都能登錄成功,接下來咱們看一下,非法用戶的登錄

非法登陸.png

看一下服務端執行的SQL語句,就不難理解了,密碼部分被註釋掉了:

1
2
3
select name from userinfo where name= 'dyan' -- n' and password='000'
 
select name from userinfo where name= 'badguy' or 1= 1 -- y' and password='000'

這種狀況就是因爲字符串拼接查詢,形成注入,所以咱們須要使用pymysql提供的參數化查詢

1
effect_row = cursor.execute(" select name from userinfo where name=%s and password=%s ", (name,password))

這樣改完以後,咱們再看,會發現報錯,可是不會登錄成功了,看看內部執行的語句,主要是對’符號作了轉義防止注入

1
select name from userinfo where name= ''dyan\ ' -- n'' and password=''123''
使用pymysql操做
  • 執行sql
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import pymysql
 
# 建立鏈接
conn = pymysql.connect(host= '127.0.0.1', port= 3306, user= 'blog', passwd= '123456', db= 'blog', charset= 'utf8')
 
# 建立遊標, 查詢數據默認爲元組類型
cursor = conn.cursor()
 
 
# 執行SQL,並返回收影響行數
row1 = cursor.execute( "update users set password = '123'")
print(row1)
# 執行SQL,並返回受影響行數
row2 = cursor.execute( "update users set password = '456' where id > %s", ( 1,))
print(row2)
# 執行SQL,並返回受影響行數(使用pymysql的參數化語句防止SQL注入)
row3 = cursor.executemany( "insert into users(username, password, email)values(%s, %s, %s)", [( "ceshi3", '333', 'ceshi3@11.com'), ( "ceshi4", '444', 'ceshi4@qq.com')])
print(row3)
 
# 提交,否則沒法保存新建或者修改的數據
conn.commit()
  • 獲取查詢數據
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import pymysql
 
# 建立鏈接
conn = pymysql.connect(host= '127.0.0.1', port= 3306, user= 'blog', passwd= '123456', db= 'blog', charset= 'utf8')
 
# 建立遊標, 查詢數據默認爲元組類型
cursor = conn.cursor()
cursor.execute( "select * from users")
 
# 獲取第一行數據
row_1 = cursor.fetchone()
print(row_1)
# 獲取前n行數據
row_n = cursor.fetchmany( 3)
print(row_n)
  • 獲取新建立數據自增ID
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import pymysql
 
# 建立鏈接
conn = pymysql.connect(host= '127.0.0.1', port= 3306, user= 'blog', passwd= '123456', db= 'blog', charset= 'utf8')
 
# 建立遊標, 查詢數據默認爲元組類型
cursor = conn.cursor()
 
cursor.executemany( "insert into users(username, password, email)values(%s, %s, %s)", [( "ceshi3", '333', 'ceshi3@11.com'), ( "ceshi4", '444', 'ceshi4@qq.com')])
new_id = cursor.lastrowid
print(new_id)
 
 
# 提交,否則沒法保存新建或者修改的數據
conn.commit()
# 關閉遊標
cursor.close()
conn.close
  • fetch數據類型
    關於默認獲取的數據是元組類型,若是想要或者字典類型的數據,即
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import pymysql
 
# 建立鏈接
conn = pymysql.connect(host= '127.0.0.1', port= 3306, user= 'blog', passwd= '123456', db= 'blog', charset= 'utf8')
 
# 遊標設置爲字典類型
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
# 左鏈接查詢
r = cursor.execute( "select * from users as u left join articles as a on u.id = a.user_id where a.user_id = 2")
result = cursor.fetchall()
print(result)
 
# 查詢一個表的全部字段名
c = cursor.execute( "SHOW FULL COLUMNS FROM users FROM blog")
cc = cursor.fetchall()
 
# 提交,否則沒法保存新建或者修改的數據
conn.commit()
# 關閉遊標
cursor.close()
# 關閉鏈接
conn.close()

執行結果

1
[{ 'user_id': 2, 'id': 2, 'password': '456', 'email': 'xinlei2017@test.com', 'a.id': 2, 'content': '成名之路', 'title': '星光大道', 'username': 'tangtang'}]
cookie的設置

Tornado中能夠對cookie進行操做,而且還能夠對cookie進行簽名以放置僞造

  • 基本操做
1
2
3
4
5
6
7
class MainHandler(tornado.web.RequestHandler):
def get(self):
if not self.get_cookie( "mycookie"):
self.set_cookie( "mycookie", "myvalue")
self.write( "Your cookie was not set yet!")
else:
self.write( "Your cookie was set!")
  • 清除cookie
1
2
3
4
5
6
self.clear_cookie(name, path= '/', domain= None)
做用: 刪除名爲name的cookie的值
注意: 執行刪除操做以後, 並非當即清除瀏覽器端的cookie值, 而是給cookie的值設置爲空,並將其有效期改爲失效,真正刪除cookie是瀏覽器本身決定的
 
self.clear_all_cookie(path= '/',domain= None)
做用:刪除掉path爲 '/'的全部的cookie的值
  • 加密cookie(簽名)

Cookie 很容易被惡意的客戶端僞造。加入你想在 cookie 中保存當前登錄用戶的 id 之類的信息,你須要對 cookie 做簽名以防止僞造。Tornado 經過 set_secure_cookieget_secure_cookie 方法直接支持了這種功能。 要使用這些方法,你須要在建立應用時提供一個密鑰,名字爲 cookie_secret。 你能夠把它做爲一個關鍵詞參數傳入應用的設置中:

生成祕鑰的方法

1
2
3
import base64
import uuid
base64.b64encode(uuid.uuid4().bytes + uuid.uuid4().bytes)
1
2
3
4
5
6
7
8
9
10
11
12
13
set_secure_cookie: 設置一個帶有簽名和時間戳的加密cookie值
 
class MainHandler(tornado.web.RequestHandler):
def get(self):
if not self.get_secure_cookie( "mycookie"):
self.set_secure_cookie( "mycookie", "myvalue")
self.write( "Your cookie was not set yet!")
else:
self.write( "Your cookie was set!")
 
application = tornado.web.Application([
( r"/", MainHandler),
], cookie_secret= "61oETzKXQAGaYdkL5gEmGeJJFuYh7EQnp2XdTP1o/Vo=")
cookie的應用

使用 cookie 記錄頁面的訪問次數

1
2
3
4
5
6
7
8
9
10
class NumHandler(RequestHandler):
def get(self, *args, **kwargs):
count = self.get_cookie( 'num', None)
if count:
count = int(count)
count = count + 1
else:
count = 1
self.set_cookie( 'num', str(count))
self.render( 'cookienum.html', num = count)
XSRF

跨站請求僞造攻擊

配置

1
2
3
4
5
6
7
settings = {
"xsrf_cookies": True,
}
application = tornado.web.Application([
( r"/", MainHandler),
( r"/login", LoginHandler),
], **settings)

使用

1
2
3
4
5
6
<form action="/new_message" method="post">
{{ xsrf_form_html() }}
{% module xsrf_form_html() %}
<input type="text" name="message"/>
<input type="submit" value="Post"/>
</form>
同步阻塞
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import tornado.httpserver
import tornado.ioloop
import tornado.web
import tornado.httpclient
 
class MainHandler(tornado.web.RequestHandler):
 
def get(self):
print( '開始')
http_client = tornado.httpclient.HTTPClient()
response = http_client.fetch( "http://www.google.com")
self.write( 'done')
 
 
def make_app():
return tornado.web.Application([
( r"/main", MainHandler)
])
 
if __name__ == "__main__":
tornado.options.parse_command_line()
app = tornado.web.Application(handlers=[
( r"/main", MainHandler)
])
 
app = make_app()
 
 
http_server = tornado.httpserver.HTTPServer(app)
http_server.listen( 8002)
tornado.ioloop.IOLoop.instance().start()
異步非阻塞
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import tornado.httpserver
import tornado.ioloop
import tornado.web
import tornado.httpclient
import time
from tornado import gen
 
class MainHandler(tornado.web.RequestHandler):
@gen.coroutine
def get(self):
print( '開始')
http_client = tornado.httpclient.AsyncHTTPClient()
yield http_client.fetch( "http://www.google.com",
callback=self.on_fetch)
 
def on_fetch(self, response):
self.write( 'done')
self.finish()
 
 
if __name__ == "__main__":
app = tornado.web.Application(handlers=[
( r"/", MainHandler)
])
http_server = tornado.httpserver.HTTPServer(app)
http_server.listen( 8001)
tornado.ioloop.IOLoop.instance().start()
Future實現
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import tornado.httpserver
import tornado.ioloop
import tornado.web
import tornado.httpclient
import tornado.gen
 
from tornado.concurrent import Future
 
fu = None
 
class SleepHandler(tornado.web.RequestHandler):
 
@tornado.gen.coroutine
def get(self):
global fu
fu = Future()
fu.add_done_callback(self.on_fetch)
yield fu
 
def on_fetch(self, response):
self.write( '終於等到你')
self.finish()
 
class TestHandler(tornado.web.RequestHandler):
 
def get(self):
fu.set_result( '666')
 
 
if __name__ == "__main__":
app = tornado.web.Application(handlers=[
( r"/sleep", SleepHandler),
( r"/test", TestHandler)
])
http_server = tornado.httpserver.HTTPServer(app)
http_server.listen( 8003)
tornado.ioloop.IOLoop.instance().start()
相關文章
相關標籤/搜索