helloworldjavascript
Tornado特色一句话简介:Tornado是非阻塞式的Web服务器,速度很是快,每秒能够处理数以千计的连接,所以Tornado是实时Web服务的一个理想框架。Tornado由于其轻量级和可扩展的特性,被使用于大量的应用和工具中。css
安装Tornado使用pip安装便可:pip install tornadohtml
运行Tornado的helloworld所需的基本组成java
#!/usr/bin/env python # -*- coding: utf-8 -*- import tornado.web import tornado.ioloop # 用于处理网页的请求 class MainHandler(tornado.web.RequestHandler): def get(self, *args, **kwargs): self.write('Hello Tornado!') # 设置不一样路由的网页对应的处理类 app = tornado.web.Application([ (r'/', MainHandler), ]) # 开始主程序I/O循环 if __name__ == '__main__': app.listen(8888) tornado.ioloop.IOLoop.instance().start()
1) app.listen(8888):设置服务器监听的端口,这里能够随意设置可用的port,好比:8080;python
2) tornado.ioloop.IOLoop.instance().start():开启I/O循环,响应客户端的操做;jquery
3) tornado.web.Application:实例化一个web应用类,用于处理用户的请求,可传入一个列表,列表中每一个元素由一个访问路由和对应的处理类构成;web
4) tornado.web.RequestHandler:定义请求处理类,用于处理对应的请求;正则表达式
Application操做数据库
一、Application:tornado.web.Aplication新建一个应用,可经过直接实例化这个类或实例化它的子类来新建应用;浏览器
二、handlers:实例化时至少须要传入参数handlers,handlers为元素为元组的列表,元组中第一个元素为路由,第二个元素为路由对应的RequestHandler处理类;路由为正则表达式,当正则表达式中有分组时,访问时会将分组的结果当作参数传入模板中;
三、settings:还有一个实例化时常常用到的参数settings,这个参数是一个字典:
四、数据库:能够在Application的子类中链接数据库“self.db = database”,而后在每一个RequestHandler中均可以使用链接的数据库“db_name = self.application.db.database”。
1 # 定义了模板路径和静态文件路径后,在使用到模板和静态文件的地方就不须要逐一添加和修改了 2 settings = { 3 'template_path': os.path.join(os.path.dirname(__file__), 'templates'), 4 'static_path': os.path.join(os.path.dirname(__file__), 'static'), 5 'debug': True, 6 } 7 8 # 对应的RequestHandler类代码未贴出来 9 app = tornado.web.Application( 10 handlers=[(r'/',MainHandler), 11 (r'/home', HomePageHandler), 12 (r'/demo/([0-9Xx\-]+)', DemoHandler), # 有分组时会将分组结果当参数传入对应的模板中 13 ], 14 **settings 15 )
1 class Application(tornado.web.Application): 2 def __init__(self): 3 handlers = [ 4 (r'/', MainHandler), 5 (r'/demo/([0-9Xx\-]+)', DemoHandler), 6 ] 7 8 settings = dict( 9 template_path=os.path.join(os.path.dirname(__file__), 'templates'), 10 static_path=os.path.join(os.path.dirname(__file__), 'static'), 11 ui_modules={'Mymodule': Mymodule}, 12 debug=True, 13 ) 14 15 # 这里使用的数据库是MongoDB,Python有对应的三方库pymongo做为驱动来链接MongoDB数据库 16 conn = pymongo.MongoClient() 17 self.db = conn['demo_db'] 18 tornado.web.Application.__init__(self, handlers, **settings) 19 20 class DemoHandler(tornado.web.RequestHandler): 21 def get(self): 22 demo_db = self.application.db.demo_db # 直接使用链接的数据库 23 sets = demo_db.find() 24 self.render( 25 'demo.html', 26 sets=sets, 27 ) 28 29 class Mymodule(tornado.web.UIModule): 30 def render(self): 31 return self.render_string('modules/mod.html',) 32 33 # 定义css文件路径 34 def css_files(self): 35 return '/static/css/style.css' 36 37 # 定义js文件路径 38 def javascript_files(self): 39 return '/static/js/jquery-3.2.1.js'
RequestHandler操做
在Application中定义了路由及对应的RequestHandler处理类后,浏览器中输入对应的URL后,返回的页面实际上是通过RequestHandler处理后render的页面;即RequestHandler用于处理请求,程序会为每个请求建立一个RequestHandler对象,而后调用对应的HTTP方法。
RequestHandler类中经常使用的方法,在子类中可根据须要进行重写:
1 # 一个简单的RequestHandler子类示例 2 class DemoHandler(tornado.web.RequestHandler): 3 # 处理get方式的请求,并跳转到demo页面 4 def get(self, arg_get=None): 5 if arg_get is None: 6 arg_get = 'test arg' 7 self.render( 8 'demo.html', 9 arg_demo=arg_get, 10 ) 11 12 # 获取post方式请求中提交的参数demo_title,若是没有内容就重定向到demo页面 13 def post(self): 14 arg_title = self.get_argument('demo_title', None) 15 if arg_title is None: 16 self.redirect('/demo/')
UIModule
tornado.web.UIModule子类为自定义的UI模板,在HTML中使用{% module module_name %}时会自动包含module_name类中render方法返回的字符串(通常为包含HTML标签内容的字符串),若是是HTML文件,返回的就是HTML模板中内容的字符串,返回的HTML内容字符串的“style”能够由其余的方法设置。
继承UIModule时通常须要重写render方法,以render_string方法返回特定格式HTML内容。
UIModule类中经常使用的方法,能够重写这些方法以知足本身对HTML格式的要求:
1 # 自定模板,可将其设置的HTML内容嵌入到其余HTML模板中 2 class MyModule(tornado.web.UIModule): 3 def render(self, arg_mod): 4 return self.render_string( 5 'module/mod.html', 6 arg_mod=arg_mod, 7 ) 8 9 # 设置外部的css文件 10 def css_files(self): 11 return '/static/css/style.css' 12 13 # 设置外部的js文件 14 def javascript_files(self): 15 return '/static/js/jquery-3.2.1.js'
Tornado异步
同步:同步的意思就像是代码的顺序执行,当这一行的代码没有执行完时,就会一直等它,直到它执行完了再执行下一行,因此遇到耗时较长的代码行时,这行代码说不定就是整个程序的“锅”了;
阻塞:当程序在某一处代码“停住”时,其余的代码就不能执行了,程序就阻塞在了此处了,好比同步的程序中卡在某行耗时长的代码时,这行代码就阻塞了后面代码的执行;
异步:异步就像是你作你的,我不用等你,你作完了天然会往下执行,我仍是本身作本身的,好比web中,当一个请求没有处理完时,程序还能够同时处理另外一个请求,不用等你这请求处理完再来处理下一个请求,因此在访问者较多时,异步的优点就不言而喻了。
Tornado的异步处理主要在如下几点(能够保持当前客户端链接不关闭,没必要等当前请求处理完成后再处理下一个请求):
一、异步装饰器@tornado.web.asynchronous:在web方法好比get上使用异步装饰器代表这是一个异步处理方法,被这个装饰器装饰的方法永远不会本身关闭客户端链接(Tornado默认在处理函数返回时自动关闭链接),必须使用finish方法手动关闭;
二、异步HTTP客户端处理类tornado.httpclient.AsyncHTTPClient:
1).AsyncHTTPClient的实例能够执行异步的HTTP请求;
2).AsyncHTTPClient的fetch方法不会返回url的调用结果(HTTPResponse),而是使用了一个callback参数来指定HTTPResponse的处理方法,将HTTPResponse做为这个指定方法的参数传入进去进行相关的处理;
3).在fetch方法的callback参数指定的处理方法的结尾处能够调用tornado.web.RequestHandler的finish方法来关闭客户端连接。
三、异步处理模块tornado.gen:
1).装饰器@tornado.gen.engine:告诉tornado被装饰的方法将使用tornado.gen.Task类;
2).使用yield生成tornado.gen.Task类的实例,将咱们想要的调用(好比:fetch方法)和须要传入该调用函数的参数传入这个Task实例:相比于第2点的fetch方法的callback回调功能将处理方法分红两个部分,这个异步生成器的好处在于,一是该请求的处理会在yield处中止执行,直到这个回调函数返回,但这并不会影响其余请求的处理,它依然是异步的,二是回调函数中可能还有回调函数,这样循环下去不容易维护,可是这个异步生成器可让全部处理都在一个方法里,易开发和维护;
同步示例:当运行如下代码时,在两个窗口短期内(10s)访问如http://localhost:8000/?word=tornado时,在第一个窗口(请求)未加载出来时(这儿设置了等待10s),第二个窗口(请求)没法处理并加载,第一个窗口加载完成后,第二个窗口也要等待10s后才能加载出来,至关于第二个窗口等待了20s。
1 #!/usr/bin/env python 2 # -*- coding: utf-8 -*- 3 4 import tornado.httpserver 5 import tornado.ioloop 6 import tornado.options 7 import tornado.web 8 import tornado.httpclient 9 10 import urllib 11 import time 12 13 from tornado.options import define, options 14 15 define('port', default=8000, help='run on the given port', type=int) 16 17 18 class IndexHandler(tornado.web.RequestHandler): 19 def get(self): 20 query = self.get_argument('word') 21 client = tornado.httpclient.HTTPClient() 22 response = client.fetch('https://www.baidu.com/baidu?' + 23 urllib.urlencode({'word': query})) 24 time.sleep(10) # 在这个请求未处理完以前,其余的请求只能“排队” 25 self.write('<h1>Tornado Synchronous Test(request_time): %s</h1>' % response.request_time) 26 27 28 if __name__ == '__main__': 29 tornado.options.parse_command_line() # 能够从命令行解析命令 30 app = tornado.web.Application(handlers=[(r'/', IndexHandler)]) 31 http_server = tornado.httpserver.HTTPServer(app) 32 http_server.listen(options.port) 33 tornado.ioloop.IOLoop.instance().start()
异步示例:与同步的代码相比,不一样之处在于三点:①@tornado.web.asynchronous,②tornado.httpclient.AsyncHTTPClient(),③fetch的callback参数(须要在回调函数中使用self.finish(),否则浏览器不会将处理结果加载出来,由于它不知道你已经处理完了)。一样运行代码发现访问如http://localhost:8000/?word=tornado时,若是开了两个窗口,这两个窗口都只需等待10s就可加载出来,第二个窗口无需等待第一个窗口加载完成。
1 #!/usr/bin/env python 2 # -*- coding: utf-8 -*- 3 4 import tornado.httpserver 5 import tornado.ioloop 6 import tornado.options 7 import tornado.web 8 import tornado.httpclient 9 10 import urllib 11 import time 12 13 from tornado.options import define, options 14 15 define('port', default=8000, help='run on the given port', type=int) 16 17 18 class IndexHandler(tornado.web.RequestHandler): 19 @tornado.web.asynchronous 20 def get(self): 21 query = self.get_argument('word') 22 client = tornado.httpclient.AsyncHTTPClient() 23 client.fetch('https://www.baidu.com/baidu?' + 24 urllib.urlencode({'word': query}), 25 callback=self.response_process) 26 27 def response_process(self, response): 28 time.sleep(10) # 在这个请求未处理完以前,其余的请求不用“排队”,能够直接处理 29 self.write('<h1>Tornado Synchronous Test(request_time): %s</h1>' % response.request_time) 30 self.finish() 31 32 33 if __name__ == '__main__': 34 tornado.options.parse_command_line() # 能够从命令行解析命令 35 app = tornado.web.Application(handlers=[(r'/', IndexHandler)]) 36 http_server = tornado.httpserver.HTTPServer(app) 37 http_server.listen(options.port) 38 tornado.ioloop.IOLoop.instance().start()
异步生成器示例:异步效果与以前的代码同样,但与以前的异步代码相比,不一样之处在于:①tornado.gen,②@tornado.gen.engine(注意调用顺序),③response是经过yield返回的。与以前的异步代码优点在于易于维护,以前的异步代码可能在callback的回调中还有callback回调,若是回调太多就很很差维护了。
1 #!/usr/bin/env python 2 # -*- coding: utf-8 -*- 3 4 import tornado.httpserver 5 import tornado.ioloop 6 import tornado.options 7 import tornado.web 8 import tornado.httpclient 9 import tornado.gen 10 11 import urllib 12 import time 13 14 from tornado.options import define, options 15 16 define('port', default=8000, help='run on the given port', type=int) 17 18 19 class IndexHandler(tornado.web.RequestHandler): 20 # 如下装饰器的使用顺序不能乱 21 @tornado.web.asynchronous 22 @tornado.gen.engine 23 def get(self): 24 query = self.get_argument('word') 25 client = tornado.httpclient.AsyncHTTPClient() 26 response = yield tornado.gen.Task(client.fetch, 27 'https://www.baidu.com/baidu?' + 28 urllib.urlencode({'word': query}) 29 ) 30 time.sleep(10) 31 self.write('<h1>Tornado Synchronous Test(request_time): %s</h1>' % response.request_time) 32 self.finish() 33 34 35 if __name__ == '__main__': 36 tornado.options.parse_command_line() # 能够从命令行解析命令 37 app = tornado.web.Application(handlers=[(r'/', IndexHandler)]) 38 http_server = tornado.httpserver.HTTPServer(app) 39 http_server.listen(options.port) 40 tornado.ioloop.IOLoop.instance().start()
Tronado的web应用安全(cookie和CSRF/XSRF)
安全cookies是web应用的安全防范之一,浏览器中的cookies存储了用户的我的信息,固然包括了某些重要的敏感的信息,若是一些恶意的脚本获得甚至修改了用户的cookies的信息,用户的信息就得不到安全的保障,因此应该对用户的cookies进行保护。Tornado的安全cookies能够对cookies签名进行安全加密,以检查cookies是否被修改过,由于恶意脚本不知道安全密钥,因此没法修改(可是恶意脚本仍然能够截获cookies来“冒充”用户,只是不能修改cookies而已,这也是另一个安全隐患,本文并不讨论这点)。
Tornado的get_secure_cookie()和set_secure_cookie()能够安全的获取和发送浏览器的cookies,能够防止浏览器中的恶意修改,可是为了使用这个功能,必须在tornado.web.Application的settings中设置cookie_secret,其值为一个惟一的随机字符串(好比:base64.b64encode(uuid.uuid4().bytes + uuid.uuid4().bytes)能够产生一个惟一的随机字符串)。
CSRF或XSRF,即跨站请求伪造,是web应用都涉及到的一个安全漏洞,它利用了浏览器的一个安全漏洞:浏览器容许恶意攻击者在受害者网站注入脚本使未受权请求表明一个已登陆用户。即别人能够“冒充”你作一些事情,而服务器也会认为这些操做是你作的。
为了防止XSRF攻击,应该注意:一是开发者考虑某些重要的请求时须要使用POST方法,二是Tornado的一个防范伪造POST的功能(这个功能是一种策略,tornado也实现了这种策略),就是在每一个请求中包含一个参数值(隐藏的HTML表单元素值)和存储的cookie值,若二者(称之为令牌)匹配上了,则证实请求有效,当某个不可信的站点没有访问cookie数据的权限时,它就不能在请求中包含这个令牌cookie值,天然就没法发送有效的请求了。tornado中使用这个功能须要在tornado.web.Application的settings中设置xsrf_cookies,值为True,同时必须在HTML的表单中包含xsrf_form_html()函数,以此造成cookie令牌。
tornado的用户验证可使用装饰器@tornado.web.authenticated,使用这个装饰器时需注意:①必须重写get_current_user()方法,这个返回的值将赋给self.current_user,②被装饰的方法被执行前会检查self.current_user的bool值是否为False(默认是None),若为False则会重定向到tornado.web.Application的settings中login_url指定的URL,③须要在tornado.web.Application的settings中login_url的URL,以便用户验证self.current_user以前能够重定向到这个URL。④当Tornado构建重定向URL时,它还会给查询字符串添加一个next参数,其值为重定向以前的URL,可使用如self.redirect(self.get_argument('next', '/'))这样的语句在用户验证成功后回到原来的页面。
1 #!/usr/bin/env python 2 # -*- coding: utf-8 -*- 3 import tornado.httpserver 4 import tornado.ioloop 5 import tornado.web 6 import tornado.options 7 import os.path 8 import base64 9 import uuid 10 11 from tornado.options import define, options 12 13 define('port', default=8000, help='run on the given port', type=int) 14 15 16 class BaseHandler(tornado.web.RequestHandler): 17 def get_current_user(self): 18 """重写此方法,返回的值将赋给self.current_user""" 19 return self.get_secure_cookie('username') # 获取cookies中username的值 20 21 22 class LoginHandler(BaseHandler): 23 def get(self): 24 self.render('login.html') 25 26 def post(self): 27 self.set_secure_cookie('username', self.get_argument('username')) # 将请求中的username值赋给cookie中的username 28 self.redirect('/') 29 30 31 class HomeHandler(BaseHandler): 32 @tornado.web.authenticated # 使用此装饰器必须重写方法get_current_user 33 def get(self): 34 """被@tornado.web.authenticated装饰的方法被执行前会检查self.current_user的bool值是否为False,不为False时才会执行此方法""" 35 self.render('index.html', user=self.current_user) 36 37 38 class LogoutHandler(BaseHandler): 39 def get(self): 40 if self.get_argument('logout', None): 41 self.clear_cookie('username') # 清楚cookies中名为username的cookie 42 self.redirect('/') 43 44 45 if __name__ == '__main__': 46 tornado.options.parse_command_line() 47 48 settings = { 49 'template_path': os.path.join(os.path.dirname(__file__), 'templates'), 50 'cookie_secret': base64.b64encode(uuid.uuid4().bytes + uuid.uuid4().bytes), # 设置cookies安全,值为一个惟一的随机字符串 51 'xsrf_cookies': True, # 设置xsrf安全,设置了此项后必须在HTML表单中包含xsrf_form_html() 52 'login_url': '/login' # 当被@tornado.web.authenticated装饰器包装的方法检查到self.current_user的bool值为False时,会重定向到这个URL 53 } 54 55 application = tornado.web.Application([ 56 (r'/', HomeHandler), 57 (r'/login', LoginHandler), 58 (r'/logout', LogoutHandler) 59 ], **settings) 60 61 http_server = tornado.httpserver.HTTPServer(application) 62 http_server.listen(options.port) 63 tornado.ioloop.IOLoop.instance().start()
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Welcom Back!</title> 6 </head> 7 <body> 8 <h1>Welcom back, {{ user }}</h1> 9 </body> 10 </html>
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Please Log In</title> 6 </head> 7 <body> 8 <form action="/login" method="POST"> 9 {% raw xsrf_form_html() %}<!-- 这实际上是一个隐藏的<input>元素,定义了“_xsrf”的值,会检查POST请求以防止跨站点请求伪造 --> 10 Username: <input type="text" name="username" /> 11 <input type="submit" value="Log In" /> 12 </form> 13 </body> 14 </html>