5 构建Tornado网站应用

       一个Tornado 网站应用通常由一个或多个RequestHanlde的子类、一个负责将请求路由至handlers的Application以及一个启动服务器的main()函数等组成。

       一个最小的“hello world”示例:

from tornado.ioloop import IOLoop
from tornado.web import RequestHandler, Application, url

class HelloHandler(RequestHandler):
    def get(self):
        self.write("Hello, world")

def make_app():
    return Application([
        url(r"/", HelloHandler),
        ])

def main():
    app = make_app()
    app.listen(8888)
    IOLoop.current().start()

Application对象

          Application这个对象负责全局的配置,包括把请求映射到Handlers的路由表(Route Table)。

          路由表URLSpec对象集合(或者元组),包含一个正则表达式和Handler类。如果正则表达式中包含捕获组,这些组将会是路径参数(path arguments),而且这些捕获的组的值会传递hanlder的Http方法的参数中。如果将字典对象作为第三个参数传递的话,这个参数的值将会传递给RequestHandler的initialize的参数。最后,URLSpec可以有个名称,可以通过Request.reverse_url得到。

          示例:

# -*- coding: utf-8 -*-
from tornado.ioloop import IOLoop
from tornado.web import Application, url, RequestHandler

__author__ = ‘Administrator‘

class MainHanlder(RequestHandler):
    def get(self):
        self.write(‘<a href="%s">link to story 1</a>‘ %
                   self.reverse_url("story", "1"))

class StoryHanlder(RequestHandler):
    def initialize(self, db):
        self.db = db

    def get(self, story_id):
        self.write("this is story %s" % story_id)

db = 1

app = Application([
    url(r"/", MainHanlder),
    url(r"/story/([0-9]+)", StoryHanlder, dict(db=db), name="story")

])

def main():
    app.listen(8888)
    IOLoop.current().start()

if __name__ == "__main__":
    main()

          

       根路径被映射至MainHanlder.而“/story/数字”格式的请求则被映射成StoryHandler.这个数字会当成是字符串传递给StoryHanlder的get方法。

在游览器中输入http://localhost:8888/

       单击“link to story 1”之后,

         Application 构造函数有很多关键字参数,用来自定义应用程序的行为。具体请参考Application.settings查看完整的设置参数列表。

 

RequestHanlder的子类

        Tornado web应用程序大部分工作都是由RequestHandler的子类来完成。主要由这个类的方法来处理,比如get(),post()等。每个handler都是定义一个或者多个这样的方法来处理不同的HTTP请求。正如上面所说,这些方法参数的值,会根据路由正则表达式匹配后的值传递进来。

        在hanlder中,调用RequestHanlder.render或者RequestHanlder.write等方法来产生响应。render() 方法通过名称加载一个模板,并渲染模板。write() 方法而是针对针对非模板的输出,接收字符串、字节和字典(字典必须编码成JSON格式)等参数。

       在RequestHandler类中,定义了很多可以被子类重载的方法。所以在开发自身的应用程序时,通常会定义一个BaseHanlder类去重写write_error 和 get_current_user等方法,然后子类继承BaseHanlder而非RequeseHanlder。

 

处理请求输入

       请求的hanlder能够通过调用self.request属性访问代表当前请求的对象。请查看HttpServerRequest类的详细属性定义。

       示例

class MyFormHandler(RequestHandler):
    def get(self):
        self.write(‘<html><body><form action="/myform" method="POST">‘
                   ‘<input type="text" name="message">‘
                   ‘<input type="submit" value="Submit">‘
                   ‘</form></body></html>‘)

    def post(self):
        self.set_header("Content-Type", "text/plain")
        self.write("You wrote " + self.get_body_argument("message"))

         用HTML表单格式的请求数据会被解析,而且通过调用方法可以获取,比如get_query_argument和get_body_argument方法。

          而如果表单中字段有多个值,可以调用get_query_arguments和get_body_argumens方法。

         表单的上传的文件可以通过self.request.files获取。每个每件对象是一个字典,格式为{“filename”:..,”content_type”:..,”body”:…}.只有当文件通过form wrapper方式(比如Content-type的类型为multipart/form)上传时,files属性值才存在。如采用form wrapper格式的话,上传的数据可以通过self.request.body属性获取。默认情况下,上传的文件会缓存在内存中。如果你处理的文件太大以至于不能很好地保存在内存中时,请查看stream_request_body类装饰器。

          由于HTML表单编码的怪异性,Tornado不会将不同类型的输入参数组合一起。在特定情况下,我们不会去解析JSON请求体。应用如果喜欢用JSON去替代表单编码(form-encoding)的话,可以去重写prepare方法去解析请求。如下:

def prepare(self):
    if self.request.headers["Content-Type"].startswith("application/json"):
        self.json_args = json.loads(self.request.body)
    else:
        self.json_args = None

      

重写RequestHanlder方法

       除了get()/post()等方法外,在必要的时候,其他方法也可以被子类重写。在每一个请求中,会执行以下的调用顺序:

1. 一个新的RequestHandler对象被创建

2. Initialize()被调用,参数的值从Application对象的配置中获取。Initialize一般只保存这些参数至成员变量中,不会产生任何输出或者调用方法比如send_error.

3. prepare() 被调用。这个的实现最好共享放置在基础类中,供其他子类继承,因为不管任何的HTTP方法被使用,prepare方法都会被调用;prepare可以会产生输出,如果调用了finish(或者redirect等)方法,请求处理过程结束。

4. get(),post(),put()等任一一个方法被调用,如果URL正则表达式包含了捕获组,这些捕获的组的值都会传递给这些方法的参数;

5. 当请求处理结束后,on_finish()方法被调用。对于同步的handler,当get()(举个例子)方法返回后,被立即调用on_finish方法。

       通常情况下,重写最多的方法包括:

  • l write_error . 给用户输出错误页面的HTML;
  • l On_connection_close 当用户客户端断开连接是调用。应用可以选择去监听断开,然后去停止一些处理过程。、
  • l get_current_user. 获得当前用户。
  • l get_user_locale .针对当前用户,返回Locale对象
  • l set_default_headers .在响应中添加额外的头部。(比如自定义的服务头部)

 

错误处理

           如果hanlder发生了异常,Tornado将会调用Request.write_error去生成错误页面。tornado.web.HTTPError 可以用来产生一个具体的错误状态码,所有的其他异常返回500的状态码。

           在调试模式下,默认的错误页面包括堆栈跟踪(stack trace),在其他情况下返回一行错误的描述(比如:“500:Internal Server Error”)。为了产生自定义的错误页面,重写request.write_error方法(可能放在供其他handlers继承的基类中)。当被异常导致错误时,exc_info 被作为关键字参数传递(注意,这个异常不能保证是当前在sys.exc_info的异常,所以write_error必须使用比如traceback.format_exception 替代traceback.format_exc)。

           另外一种产生错误页面的方式为调用set_status方法,而不是write_error,然后写响应以及返回。特殊的tornado.web.Finish异常被触发会终止hanlder,而不 会调用write_error。

           针对404错误,使用在Application setting的default_handler_class来处理。这个handler必须重写prepare方法。根据上面的描述,这将会产生一个错误页面,要么触发HTTPError(404)错误以及重写write_error,要么调用self.set_status(404)然后在prepare中直接产生响应。

 

重定向

         Tornado存在两种主要的重定向请求的方式,一种是调用RequestHanlder.redirect方法,另外一种是直接使用RedirectHandler类。

         在实现的hanlder中使用self.redirect方法可以重定向到任何地方。这个方法还有一个参数permanent,用来指示这个重定向是否是永久的。这个参数的默认值为False,会产生302 Found状态编号的响应,而且特别适合像POST()请求处理成功后的重定向。如果permanent这个参数值为True, 301 Moved Permanetly状态编码会返回给用户。

         RedirectHandler ,允许用户可以在Application路由表中配置重定向的链接。比如下面一个例子,配置了单个静态重定向。

app = tornado.web.Application([
    url(r"/app", tornado.web.RedirectHandler,
        dict(url="http://itunes.apple.com/my-app-id")),
    ])

            RedirectHanlder 同样支持正则表达式的情形。下面的规则定义,可以使以/pictures/开头的请求重定向至以/photos/为前缀的请求。

app = tornado.web.Application([
    url(r"/photos/(.*)", MyPhotoHandler),
    url(r"/pictures/(.*)", tornado.web.RedirectHandler,
        dict(url=r"/photos/\1")),
    ])

        不像RequestHanlder.redirect方法,RedirectHandler默认采用永久的重定向,即permanent属性为True.这是应为路由表在运行时不能改变,而在hanlder中使用的重定向地址在逻辑上可以会改变。如果要用RedirectHandler发送一个临时的重定向,在RedirectHandler初始化参数中将permanent设置成False.

 

异步Handler

        默认情况下,hanlders是同步的。当get()/post()方法返回时,请求才认为结束,然后再发送响应。当handler正在运行时,其他请求将会被阻塞。任何一个长时间运行的hanlder必须设置为异步的,这样操作就不会阻塞。

        使hanlder成为异步的最简单的方式就是使用coroutine装饰器。使用yield关键字可以执行非阻塞的I/O操作,直到coroutine已经返回后,才能将响应返回给用户端。

        在有些情况下,coroutines没有采用回调的方式方便,这种回调方式是采用tornado.web.asynchrounous装饰器。当使用这个装饰器时,响应不会自动发送,请求会保持打开的状态直到回调函数调用了RequestHandler.finish方法。应用必须保证这个方法被调用,不然用户的游览器将会简单地暂停。

       下面是个例子,使用内置的 AsyncHttpClient调用FriendFeed Api。

class MainHandler(tornado.web.RequestHandler):
    @tornado.web.asynchronous
    def get(self):
        http = tornado.httpclient.AsyncHTTPClient()
        http.fetch("http://friendfeed-api.com/v2/feed/bret",
                   callback=self.on_response)

    def on_response(self, response):
        if response.error: raise tornado.web.HTTPError(500)
        json = tornado.escape.json_decode(response.body)
        self.write("Fetched " + str(len(json["entries"])) + " entries "
                   "from the FriendFeed API")
        self.finish()

           当get()方法返回时,这个请求没有结束。当最后调用on_response 方法之前,这个请求一直都是打开的,当调用了self.finish方法后,响应最后才会发送给客户端。

           为了对比,下面用coroutine实现的方式。

class MainHandler(tornado.web.RequestHandler):
    @tornado.gen.coroutine
    def get(self):
        http = tornado.httpclient.AsyncHTTPClient()
        response = yield http.fetch("http://friendfeed-api.com/v2/feed/bret")
        json = tornado.escape.json_decode(response.body)
        self.write("Fetched " + str(len(json["entries"])) + " entries "
                   "from the FriendFeed API")
时间: 2024-08-12 03:00:17

5 构建Tornado网站应用的相关文章

BAE tornado部署

python-web, python-worker选择 如果用于web访问,选择python-web. 其他,可选python-worker. 构建tornado的framework 创建requirements.txt 文件,将 tornado==4.2 添加到文件中(否则,会出现import错误). 创建tornado application. import tornado.wsgi import tornado.web class MainHandler(tornado.web.Reque

python使用apscheduler实现定时任务

安装apscheduler pip install apscheduler 基本概念 1.触发器triggers. 触发器包含调度逻辑.每个作业都有自己的触发器,用于确定下一个任务何时运行.触发器有三种内建的trigger: data: 特定的时间触发 interval: 固定的时间间隔触发 cron: 在特定时间周期性地触发 2. 任务存储器 job stores. 用于存放任务,把任务存放在内存(为默认MemoryJobStore)或数据库中 3.执行器 executors. 执行器是将任务

Celery,Tornado,Supervisor构建和谐的分布式系统

Celery 分布式的任务队列 与rabbitmq消息队列的区别与联系: rabbitmq 调度的是消息,而Celery调度的是任务. Celery调度任务时,需要传递参数信息,传输载体可以选择rabbitmq. 利用rabbitmq的持久化和ack特性,Celery可以保证任务的可靠性. 优点: 轻松构建分布式的Service Provider. 高可扩展性,增加worker也就是增加了队列的consumer. 可靠性,利用消息队列的durable和ack,可以尽可能降低消息丢失的概率,当wo

tornado学习精要

最简单的应用在程序的最顶部,我们导入了一些Tornado模块.虽然Tornado还有另外一些有用的模块,但在这个例子中我们必须至少包含这四个模块. 12341234包括了一个有用的模块(tornado.options)来从命令行中读取设置.我们在这里使用这个模块指定我们的应用监听HTTP请求的端口. 1212工作流程:如果一个与define语句中同名的设置在命令行中被给出,那么它将成为全局options的一个属性. 如果用户运行程序时使用了–help选项,程序将打印出所有你定义的选项以及你在de

CentOS6.4安装python2.7.3环境和Tornado

Centos6.4默认的python环境是2.6.6.我们可以自己安装Python 2.7.3. 但是值得注意的是,我们必须不能破坏系统的环境. 因为几个关键的实用应用程序依赖于Python2.6.6. 如果替换了系统的python环境就会发生很多难以预见的错误,导致要重装系统. 在没有破坏系统的python环境的情况下安装Python 2.7.3. 执行以下命令,请使 用root的身份登录或者使用sudo命令 一.安装Python 2.7.3 1. 安装开发工具 为了编辑Python,你必须要

【go】脑补框架 Express beego tornado Flux reFlux React jsx jpg-ios出品

http://goexpresstravel.com/ 今天 Express 的作者 TJ Holowaychuk 发了一篇文章,正式宣告和 Node.js 拜拜了,转向 Go 语言. Go verses Node 如果你在做分布式工作,你会发现 Go 语言丰富的并发原语非常有帮助.虽然我们用 Node 的 generator 也可以做类似的事,但在我看来,generator 永远只能做一半.没有独立的栈错误处理和报告,充其量是中等.我也不想再等(Node)社区花3 年去整理(改善),尤其是我们

微信开放平台开发(2) 网站应用微信登录

关键字:微信公众平台 微信开放平台 微信登录 微信扫码登录 使用微信账号登录网站作者:方倍工作室 原文:http://www.cnblogs.com/txw1958/p/weixin-qrlogin.html 在这篇微信公众平台开发教程中,我们将介绍如何使用微信开放平台接口实现微信扫码登录的功能. 准备工作 网站应用微信登录是基于OAuth2.0协议标准构建的微信OAuth2.0授权登录系统. 在进行微信OAuth2.在进行微信OAuth2.0授权登录接入之前,在微信开放平台注册开发者帐号,并拥

从零构建一个简单的 Python Web框架

为什么你想要自己构建一个 web 框架呢?我想,原因有以下几点: 你有一个新奇的想法,觉得将会取代其他的框架 你想要获得一些名气 你遇到的问题很独特,以至于现有的框架不太合适 你对 web 框架是如何工作的很感兴趣,因为你想要成为一位更好的 web 开发者. 接下来的笔墨将着重于最后一点.这篇文章旨在通过对设计和实现过程一步一步的阐述告诉读者,我在完成一个小型的服务器和框架之后学到了什么.你可以在这个代码仓库中找到这个项目的完整代码. 我希望这篇文章可以鼓励更多的人来尝试,因为这确实很有趣.它让

构建LAMP平台及应用系统

LAMP: LAMP是目前最为成熟的一种企业网站应用模式,可以提供动态web站点应用及开发环境,其构成为L(Linux)A(Apache)M(MySQL)P(PHP/python/perl等开发语言) 构建LAMP环境前需要提前部署好Apache服务和MySQL服务. 一,构建PHP运行环境 1.编译安装libmcrypt,mhash,mcrypt编译安装PHP软件包 确认本地已经下载好需要的工具包 确认本地服务器没有安装PHP环境 安装需要的软件包 先进行编译确认配置无误后在进行安装 确认PH