Различия между версиями 3 и 35 (по 32 версиям)
Версия 3 от 2010-06-06 22:45:51
Размер: 355
Редактор: RostislavDzinko
Комментарий:
Версия 35 от 2010-06-17 20:34:31
Размер: 25419
Редактор: RostislavDzinko
Комментарий:
Удаления помечены так. Добавления помечены так.
Строка 1: Строка 1:
= Веб сервер Tornado = = Документация к веб-серверу Tornado =
Строка 5: Строка 5:
== Содержание ==

 1. [[Документации/Tornado-web/Обзор|Обзор]]
 1. [[Документации/Tornado-web/Загрузка|Загрузка]]
   2. [[Документации/Tornado-web/Подготовка|Подготовка]]
[[http://www.tornadoweb.org|Tornado]] - открытая версия масштабируемого, неблокирующего сервера и инструментов, которые используются сервисом [[http://friendfeed.com|FriendFeed]]. Приложение [[http://friendfeed.com|FriendFeed]] написано с использованием фреймворка, который выглядит как web.py или веб приложение Google, но имеет ряд дополнительных инструментов и оптимизаций с целью получения всех выгод от низлежащей неблокирующей инфраструктуры.

<<TableOfContents()>>

= Обзор =

Веб-сервер, который обслуживает сервис [[http://friendfeed.com|FriendFeed]] относительно простой, неблокирующий веб сервер, написанный на языке Python. Приложение [[http://friendfeed.com|FriendFeed]] написано с использованием веб фреймворка written using a web framework that looks a bit like web.py or Google's webapp, but with additional tools and optimizations to take advantage of the non-blocking web server and tools.

[[http://www.tornadoweb.org|Tornado]] - открытая версия масштабируемого, неблокирующего сервера и инструментов, которые используются сервисом [[http://friendfeed.com|FriendFeed]]. Данный фреймворк отличается от большинства мейнстримовских (и, определенно, большинства Python фреймворков), потому что использование неблокирующего принципа дает достаточную быстроту. Благодаря этому, а также благодаря использованию [[http://www.kernel.org/doc/man-pages/online/pages/man4/epoll.4.html|epoll]], он может обрабатывать тысячи одновременных соединений, что значит, этот фреймворк идеален для создания веб сервисов реального времени. Мы построили веб сервер специфиWe built the web server конкретно для того, чтобы обрабатывать функции сервиса [[http://friendfeed.com|FriendFeed]] в реальном времени — каждый активный пользователь сервися поддерживает открытое соединение с серверами. (Для получения дополнительной информации о масштабировании серверов для поддержки тисяч клиентов, прочитайте о [[http://www.kegel.com/c10k.html|проблеме C10K]].)

= Загрузка =

Загрузите последнюю версию Tornado с !GitHub:

 [[http://www.tornadoweb.org/static/tornado-0.2.tar.gz|tornado-0.2.tar.gz]]

На !GitHub Вы также можете просмореть исходники. Чтобы установить Tornado:

{{{#!highlight bash
tar xvzf tornado-0.2.tar.gz
cd tornado-0.2
python setup.py build
sudo python setup.py install
}}}

После установки, вы получите возможность запустить любое из демонстрационных приложений из папки ''demos'', которые включены в пакет Tornado.

{{{#!highlight bash
./demos/helloworld/helloworld.py
}}}

== Подготовка ==

Tornado тестировался на Python 2.5 и 2.6. Для того, чтобы использовать все функции Tornado, вам нужны библиотеки !PycURL !JSON, например ''simplejson''. Полные инструкции по установке на Mac OS X и Ubuntu показаны ниже.

'''Mac OS X 10.5/10.6'''

{{{#!highlight bash
sudo easy_install setuptools pycurl==7.16.2.1 simplejson
}}}

'''Ubuntu Linux'''

{{{#!highlight bash
sudo apt-get install python-dev python-pycurl python-simplejson
}}}

= Список модулей =

Наиболее важный модуль - ''web'', который является фреймворком, включающим в себя большую часть функционала пакета Tornado. Другие модули - это инструменты, которые делают модуль ''web'' богаче. Смотрите [[#Руководство|Руководство по Tornado]] ниже для более полной информации о пакете ''web''.

== Основные модули ==

 * [[http://github.com/facebook/tornado/blob/master/tornado/web.py|web]] - веб фреймворк, на котором построен !FriendFeed. '''web''' содержит большинство важных функций Tornado
 * [[http://github.com/facebook/tornado/blob/master/tornado/escape.py|escape]] - методы кодирование/декодирование XHTML, JSON и URL
 * [[http://github.com/facebook/tornado/blob/master/tornado/database.py|database]] - Простая обертка вокруг !MySQLdb для упрощения спользования СУБД MySQL
 * [[http://github.com/facebook/tornado/blob/master/tornado/template.py|template]] - язык шаблонов, в основе которого находится язык Python
 * [[http://github.com/facebook/tornado/blob/master/tornado/httpclient.py|httpclient]] - неблокирующий HTTP клиент созданный для работы с модулями '''web''' и '''httpserver'''
 * [[http://github.com/facebook/tornado/blob/master/tornado/auth.py|auth]] - реализация схем аутентификации и авторизации от третих разработчиков (Google OpenID/OAuth, Facebook Platform, Yahoo BBAuth, !FriendFeed OpenID/OAuth, Twitter OAuth)
 * [[http://github.com/facebook/tornado/blob/master/tornado/locale.py|locale]] - поддержка локализации/интернационализации
 * [[http://github.com/facebook/tornado/blob/master/tornado/options.py|options]] - синтаксический анализатор файлов настроек и аргументов коммандной строки, оптимизированный для использования в среде сервера

== Низкоуровневые модули ==

 * [[http://github.com/facebook/tornado/blob/master/tornado/httpserver.py|httpserver]] - очень простой HTTP сервер, на основе которого построен модуль '''web'''
 * [[http://github.com/facebook/tornado/blob/master/tornado/iostream.py|iostream]] - простая обертка вокруг неблокирующих сокетов для обеспечения общих шаблонов считывания и записи
 * [[http://github.com/facebook/tornado/blob/master/tornado/ioloop.py|ioloop]] - основная петля ввода/вывода

== Другие модули ==

 * [[http://github.com/facebook/tornado/blob/master/tornado/s3server.py|s3server]] - веб сервер, который реализует большую часть интерфейса Amazon S3, с локальным файловым хранилищем данных

= Руководство =

== Обработчики и параметры запросов ==

Веб Приложение Tornado отображает (maps) URL или шаблоны URL в подклассы '''tornado.web.!RequestHandler'''. Эти классы определяют '''get()''' и '''post()''' методы для обработки запросов HTTP GET и POST по этому URL.

Этот код отображает корневой URL '''/''' в '''!MainHandler''', а шаблон URL '''/story/([0-9]+)''' в '''!StoryHandler'''. Регулярные выражения передаются в качестве аргументов методам класс '''!RequestHandler''':

{{{#!highlight python
class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("You requested the main page")

class StoryHandler(tornado.web.RequestHandler):
    def get(self, story_id):
        self.write("You requested the story " + story_id)

application = tornado.web.Application([
    (r"/", MainHandler),
    (r"/story/([0-9]+)", StoryHandler),
])
}}}

С помощью метода get_argument() вы можете получить аргументы строки запроса и распарсить тело POST запроса:

{{{#!highlight python
class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write('<html><body><form action="/" 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_argument("message"))
}}}

Если вы хотите отправить сообщение об ошибке клиенту, например, 403 Unauthorized, вам достаточно вызвать исключение '''tornado.web.HTTPError''':

{{{#!highlight python
if not self.user_is_logged_in():
    raise tornado.web.HTTPError(403)
}}}

Обработчик запросов имеет доступ к объекту, который отражает состояние текущего запроса через атрибут '''self.request'''. Объект HTTPRequest object содержит ряд полезных атрибутов, среди них:

 * arguments - все аргументы GET и POST запроса
 * files - все загруженные файлы (через запросы '''multipart/form-data POST''')
 * path - путь запроса (все, что находится в строке запроса перед '''?''')
 * headers - заголовки запроса

Смотрите определение класса HTTPRequest в модуле '''httpserver''' для получения полного списка атрибутов.

== Шаблоны ==

Вы можете использовать любой язык шаблонов, который поддерживает язык Python, но Tornado идет вместе со своим собственным, который намного быстрее и гибче чем множество популярных систем шаблонов. Для полной информации обратитесь к документации к модулю '''template'''.

Шаблон Tornado - простой (или другой текстовый формат) с вложенными управляющими последовательностями языка Python, которые являются выражениями, заключенными в разметку:

{{{#!highlight html
<html>
   <head>
      <title>{{ title }}</title>
   </head>
   <body>
     <ul>
       {% for item in items %}
         <li>{{ escape(item) }}</li>
       {% end %}
     </ul>
   </body>
 </html>
}}}

Если вы сохранили этот шаблон как "template.html", и положили в ту же папку, что и файл Python, отобразите этот шаблон следующим образом:

{{{#!highlight python
class MainHandler(tornado.web.RequestHandler):
    def get(self):
        items = ["Item 1", "Item 2", "Item 3"]
        self.render("template.html", title="My title", items=items)
}}}

Шаблоны Tornado поддерживают упаравляющие последовательности и выражения. Управляющие последовательности заключены в символы '''{%''' и '''%}''', например, '''{% if len(items) > 2 %}'''. Выражения заключены в символы '''{ { и } }''', например, '''{ { items[0] } }'''.

Управляющие последовательности более менее точно соответствують предложениям языка Python. Мы поддерживаем '''if''', '''for''', '''while''', и '''try''', каждое из которых заканчивается последовательностью символов '''{% end %}'''. Мы также поддерживаем наследование шаблонов через предложения '''extends''' и '''block''', которые более детально описаны в документации к модулю '''template'''.

Выражения могут быть любыми, соответствующими языку Python, включая вызовы функций. Мы поддерживаем функции '''escape''', '''url_escape''', и '''json_encode''' по молчанию, но вы можете передать любые другие функции в шаблон через ключевые аргументы функции '''render''' модуля '''template''':

{{{#!highlight python
class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.render("template.html", add=self.add)

    def add(self, x, y):
        return x + y
}}}

Когда вы разрабатываете реальное приложение, вам не нужны все функции шаблонов Tornado, особенно наследование. Почитайте о всех функциях шаблонов в документации к модулю '''template'''.

Под капотом, шаблоны Tornado транслируются прямо в Python. Выражения из шаблонов копируются без изменений в Python функцию, которая представляет шаблон. Мы не пытаемся предостеречь вас от каких либо действий в языке шаблонов; мы создали его для того, чтобы обеспечивать гибкость, которая не свойственна другим, более строгим языкам шаблонов. Следовательно, если вы напишите внутри выражений какую нибудь несуразицу, то получите ошибки Python при выполнении шаблона.

== Cookies и защищенные cookies ==

You can set cookies in the user's browser with the set_cookie method:

{{{#!highlight python
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!")
}}}

Cookies are easily forged by malicious clients. If you need to set cookies to, e.g., save the user ID of the currently logged in user, you need to sign your cookies to prevent forgery. Tornado supports this out of the box with the set_secure_cookie and get_secure_cookie methods. To use these methods, you need to specify a secret key named cookie_secret when you create your application. You can pass in application settings as keyword arguments to your application:

{{{#!highlight python
application = tornado.web.Application([
    (r"/", MainHandler),
], cookie_secret="61oETzKXQAGaYdkL5gEmGeJJFuYh7EQnp2XdTP1o/Vo=")
}}}

Signed cookies contain the encoded value of the cookie in addition to a timestamp and an HMAC signature. If the cookie is old or if the signature doesn't match, get_secure_cookie will return None just as if the cookie isn't set. The secure version of the example above:

{{{#!highlight python
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!")
}}}

== Аутентификация пользователей ==

== Защита от CSRF ==

== Статические файлы и агрессивное кеширование файлов ==

== Локализация ==

== Модули пользовательского интерфейса ==

Для облегчения поддержки стандартных и многократно используемых виджетов вашими приложениями, в торнадо включена поддержка модулей пользовательского интерфейса (UI Modules). По сути, они являются особыми функциональными запросами на отображение компонентов вашей страницы и они идут в комплекте с собственными таблицами CSS и функциями JavaScript.

Допустим, что у нас есть блог и мы хотим, чтобы записи этого блога присутствовали как на главной странице, так и на отдельных страницах записей. Для этого вы можете сделать класс '''Entry''', унаследовав его от '''tornado.web.UIModule''', вызывая который вы сможете отображать модуль записи на любой типе страницы.
Для начала, создайте отдельный файл python в котором мы будем хранить модули интерфейса: '''uimodules.py''' и разместите в него следующий код:

{{{#!highlight python
class Entry(tornado.web.UIModule):
    def render(self, entry, show_comments=False):
        return self.render_string(
            "module-entry.html", show_comments=show_comments)
}}}


В настройках приложения укажите '''uimodules.py''' в качестве файла с модулями интерфейса, используя опцию '''ui_modules'''.

{{{#!highlight python
class HomeHandler(tornado.web.RequestHandler):
    def get(self):
        entries = self.db.query("SELECT * FROM entries ORDER BY date DESC")
        self.render("home.html", entries=entries)

class EntryHandler(tornado.web.RequestHandler):
    def get(self, entry_id):
        entry = self.db.get("SELECT * FROM entries WHERE id = %s", entry_id)
        if not entry: raise tornado.web.HTTPError(404)
        self.render("entry.html", entry=entry)

settings = {
    "ui_modules": uimodules,
}
application = tornado.web.Application([
    (r"/", HomeHandler),
    (r"/entry/([0-9]+)", EntryHandler),
], **settings)
}}}

В основном шаблоне '''home.html''' вы не включаете html-код напрямую, а ссылаетесь на модуль '''Entry'''

{{{#!highlight html
{% for entry in entries %}
  {{ modules.Entry(entry) }}
{% end %}
}}}

В шаблоне отдельной записи '''entry.html''' вы также ссылаетесь на модуль '''Entry''', но уже с аргументом '''show_comments''' , чтобы показать расширенный вариант модуля записи:

{{{#!highlight html
{{ modules.Entry(entry, show_comments=True) }}
}}}

В модули могут включаться произвольные CSS таблицы стилей и функции JavaScript посредством переопределеня методов '''embedded_css''', '''embedded_javascript''', '''javascript_files''' или '''css_files'''.

{{{#!highlight python
class Entry(tornado.web.UIModule):
    def embedded_css(self):
        return ".entry { margin-bottom: 1em; }"

    def render(self, entry, show_comments=False):
        return self.render_string(
            "module-entry.html", show_comments=show_comments)
}}}

Модули CSS и JavaScript будут подключены к итоговой странице единократно, вне зависимости от количества вызовов модуля пользовательского интерфейса в пределах нее. CSS будет включена в элемент '''<head>''' страницы, а JavaScript будет размещен прямо перед тегом '''</body>''' в конце страницы.

== Неблокирующие, асинхронные запросы ==

Обычно, когда заканчивается выполнение обработчика HTTP запроса, запрос автоматически завершается. Так как Tornado использует неблокирующий стиль ввода-вывода, но вы можете переназначить стандартное поведение, если хотите чтобы запрос оставался открытым после возврата из основного метода обработчика, используя декоратор класса-обработчика '''tornado.web.asynchronous'''.

При его использовании вы должны сами вызвать self.finish(), чтобы завершить HTTP запрос, или вы создадите лишнюю нагрузку на браузер пользователя или "повесите" его.

{{{#!highlight python
class MainHandler(tornado.web.RequestHandler):
    @tornado.web.asynchronous
    def get(self):
        self.write("Hello, world")
        self.finish()
}}}
  
Ниже приведен рабочий пример, который делает обращение к FriendFeed API, используя встроенный асинхронный HTTP клиент Торнадо:

{{{#!highlight python
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.async_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()''' возвращает управление, HTTP запрос остается незавершенным. Когда, наконец, вызывается метод '''on_responce()''', запрос все еще открыт. В конце метода ответа мы должны отдать браузеру результат выполнения обработчика и закрыть соединение посредством вызова '''self.finish()'''.

Если вы вызываете асинхронные библиотечные функции, которые требуют ответа (например функция HTTP fetch в примере выше), вы должны обязательно обернуть их self.async_callback. Эта простая обертка гарантирует, что если ваша функция создаст исключение или программную ошибку, то браузеру будет послан соответствующий HTTP-ответ с кодом ошибки и соединение будет корректно завершено.

В качестве более развернутого примера вы можете посмотреть приложение '''chat''', идущее в качестве примера с Tornado, которое является чатом на основе AJAX и использует т.н. [[http://en.wikipedia.org/wiki/Push_technology#Long_polling|длинные запросы]] (long polling)

== Аутентификация от третих разработчиков ==

= Производительность =

= Запуск Tornado на производстве =

= WSGI и Google AppEngine =

= Предостережения и поддержка =

'''Перевод: Ростислав Дзинько, Илья Кутуков'''

Документация к веб-серверу Tornado

http://www.tornadoweb.org/static/tornado.png

Tornado - открытая версия масштабируемого, неблокирующего сервера и инструментов, которые используются сервисом FriendFeed. Приложение FriendFeed написано с использованием фреймворка, который выглядит как web.py или веб приложение Google, но имеет ряд дополнительных инструментов и оптимизаций с целью получения всех выгод от низлежащей неблокирующей инфраструктуры.

Обзор

Веб-сервер, который обслуживает сервис FriendFeed относительно простой, неблокирующий веб сервер, написанный на языке Python. Приложение FriendFeed написано с использованием веб фреймворка written using a web framework that looks a bit like web.py or Google's webapp, but with additional tools and optimizations to take advantage of the non-blocking web server and tools.

Tornado - открытая версия масштабируемого, неблокирующего сервера и инструментов, которые используются сервисом FriendFeed. Данный фреймворк отличается от большинства мейнстримовских (и, определенно, большинства Python фреймворков), потому что использование неблокирующего принципа дает достаточную быстроту. Благодаря этому, а также благодаря использованию epoll, он может обрабатывать тысячи одновременных соединений, что значит, этот фреймворк идеален для создания веб сервисов реального времени. Мы построили веб сервер специфиWe built the web server конкретно для того, чтобы обрабатывать функции сервиса FriendFeed в реальном времени — каждый активный пользователь сервися поддерживает открытое соединение с серверами. (Для получения дополнительной информации о масштабировании серверов для поддержки тисяч клиентов, прочитайте о проблеме C10K.)

Загрузка

Загрузите последнюю версию Tornado с GitHub:

На GitHub Вы также можете просмореть исходники. Чтобы установить Tornado:

   1 tar xvzf tornado-0.2.tar.gz
   2 cd tornado-0.2
   3 python setup.py build
   4 sudo python setup.py install

После установки, вы получите возможность запустить любое из демонстрационных приложений из папки demos, которые включены в пакет Tornado.

   1 ./demos/helloworld/helloworld.py

Подготовка

Tornado тестировался на Python 2.5 и 2.6. Для того, чтобы использовать все функции Tornado, вам нужны библиотеки !PycURL !JSON, например simplejson. Полные инструкции по установке на Mac OS X и Ubuntu показаны ниже.

Mac OS X 10.5/10.6

   1 sudo easy_install setuptools pycurl==7.16.2.1 simplejson

Ubuntu Linux

   1 sudo apt-get install python-dev python-pycurl python-simplejson

Список модулей

Наиболее важный модуль - web, который является фреймворком, включающим в себя большую часть функционала пакета Tornado. Другие модули - это инструменты, которые делают модуль web богаче. Смотрите Руководство по Tornado ниже для более полной информации о пакете web.

Основные модули

  • web - веб фреймворк, на котором построен FriendFeed. web содержит большинство важных функций Tornado

  • escape - методы кодирование/декодирование XHTML, JSON и URL

  • database - Простая обертка вокруг !MySQLdb для упрощения спользования СУБД MySQL

  • template - язык шаблонов, в основе которого находится язык Python

  • httpclient - неблокирующий HTTP клиент созданный для работы с модулями web и httpserver

  • auth - реализация схем аутентификации и авторизации от третих разработчиков (Google OpenID/OAuth, Facebook Platform, Yahoo BBAuth, FriendFeed OpenID/OAuth, Twitter OAuth)

  • locale - поддержка локализации/интернационализации

  • options - синтаксический анализатор файлов настроек и аргументов коммандной строки, оптимизированный для использования в среде сервера

Низкоуровневые модули

  • httpserver - очень простой HTTP сервер, на основе которого построен модуль web

  • iostream - простая обертка вокруг неблокирующих сокетов для обеспечения общих шаблонов считывания и записи

  • ioloop - основная петля ввода/вывода

Другие модули

  • s3server - веб сервер, который реализует большую часть интерфейса Amazon S3, с локальным файловым хранилищем данных

Руководство

Обработчики и параметры запросов

Веб Приложение Tornado отображает (maps) URL или шаблоны URL в подклассы tornado.web.RequestHandler. Эти классы определяют get() и post() методы для обработки запросов HTTP GET и POST по этому URL.

Этот код отображает корневой URL / в MainHandler, а шаблон URL /story/([0-9]+) в StoryHandler. Регулярные выражения передаются в качестве аргументов методам класс RequestHandler:

   1 class MainHandler(tornado.web.RequestHandler):
   2     def get(self):
   3         self.write("You requested the main page")
   4 
   5 class StoryHandler(tornado.web.RequestHandler):
   6     def get(self, story_id):
   7         self.write("You requested the story " + story_id)
   8 
   9 application = tornado.web.Application([
  10     (r"/", MainHandler),
  11     (r"/story/([0-9]+)", StoryHandler),
  12 ])

С помощью метода get_argument() вы можете получить аргументы строки запроса и распарсить тело POST запроса:

   1 class MainHandler(tornado.web.RequestHandler):
   2     def get(self):
   3         self.write('<html><body><form action="/" method="post">'
   4                    '<input type="text" name="message">'
   5                    '<input type="submit" value="Submit">'
   6                    '</form></body></html>')
   7 
   8     def post(self):
   9         self.set_header("Content-Type", "text/plain")
  10         self.write("You wrote " + self.get_argument("message"))

Если вы хотите отправить сообщение об ошибке клиенту, например, 403 Unauthorized, вам достаточно вызвать исключение tornado.web.HTTPError:

   1 if not self.user_is_logged_in():
   2     raise tornado.web.HTTPError(403)

Обработчик запросов имеет доступ к объекту, который отражает состояние текущего запроса через атрибут self.request. Объект HTTPRequest object содержит ряд полезных атрибутов, среди них:

  • arguments - все аргументы GET и POST запроса
  • files - все загруженные файлы (через запросы multipart/form-data POST)

  • path - путь запроса (все, что находится в строке запроса перед ?)

  • headers - заголовки запроса

Смотрите определение класса HTTPRequest в модуле httpserver для получения полного списка атрибутов.

Шаблоны

Вы можете использовать любой язык шаблонов, который поддерживает язык Python, но Tornado идет вместе со своим собственным, который намного быстрее и гибче чем множество популярных систем шаблонов. Для полной информации обратитесь к документации к модулю template.

Шаблон Tornado - простой (или другой текстовый формат) с вложенными управляющими последовательностями языка Python, которые являются выражениями, заключенными в разметку:

   1 <html>
   2    <head>
   3       <title>{{ title }}</title>
   4    </head>
   5    <body>
   6      <ul>
   7        {% for item in items %}
   8          <li>{{ escape(item) }}</li>
   9        {% end %}
  10      </ul>
  11    </body>
  12  </html>

Если вы сохранили этот шаблон как "template.html", и положили в ту же папку, что и файл Python, отобразите этот шаблон следующим образом:

   1 class MainHandler(tornado.web.RequestHandler):
   2     def get(self):
   3         items = ["Item 1", "Item 2", "Item 3"]
   4         self.render("template.html", title="My title", items=items)

Шаблоны Tornado поддерживают упаравляющие последовательности и выражения. Управляющие последовательности заключены в символы {% и %}, например, {% if len(items) > 2 %}. Выражения заключены в символы { { и } }, например, { { items[0] } }.

Управляющие последовательности более менее точно соответствують предложениям языка Python. Мы поддерживаем if, for, while, и try, каждое из которых заканчивается последовательностью символов {% end %}. Мы также поддерживаем наследование шаблонов через предложения extends и block, которые более детально описаны в документации к модулю template.

Выражения могут быть любыми, соответствующими языку Python, включая вызовы функций. Мы поддерживаем функции escape, url_escape, и json_encode по молчанию, но вы можете передать любые другие функции в шаблон через ключевые аргументы функции render модуля template:

   1 class MainHandler(tornado.web.RequestHandler):
   2     def get(self):
   3         self.render("template.html", add=self.add)
   4 
   5     def add(self, x, y):
   6         return x + y

Когда вы разрабатываете реальное приложение, вам не нужны все функции шаблонов Tornado, особенно наследование. Почитайте о всех функциях шаблонов в документации к модулю template.

Под капотом, шаблоны Tornado транслируются прямо в Python. Выражения из шаблонов копируются без изменений в Python функцию, которая представляет шаблон. Мы не пытаемся предостеречь вас от каких либо действий в языке шаблонов; мы создали его для того, чтобы обеспечивать гибкость, которая не свойственна другим, более строгим языкам шаблонов. Следовательно, если вы напишите внутри выражений какую нибудь несуразицу, то получите ошибки Python при выполнении шаблона.

Cookies и защищенные cookies

You can set cookies in the user's browser with the set_cookie method:

   1 class MainHandler(tornado.web.RequestHandler):
   2     def get(self):
   3         if not self.get_cookie("mycookie"):
   4             self.set_cookie("mycookie", "myvalue")
   5             self.write("Your cookie was not set yet!")
   6         else:
   7             self.write("Your cookie was set!")

Cookies are easily forged by malicious clients. If you need to set cookies to, e.g., save the user ID of the currently logged in user, you need to sign your cookies to prevent forgery. Tornado supports this out of the box with the set_secure_cookie and get_secure_cookie methods. To use these methods, you need to specify a secret key named cookie_secret when you create your application. You can pass in application settings as keyword arguments to your application:

   1 application = tornado.web.Application([
   2     (r"/", MainHandler),
   3 ], cookie_secret="61oETzKXQAGaYdkL5gEmGeJJFuYh7EQnp2XdTP1o/Vo=")

Signed cookies contain the encoded value of the cookie in addition to a timestamp and an HMAC signature. If the cookie is old or if the signature doesn't match, get_secure_cookie will return None just as if the cookie isn't set. The secure version of the example above:

   1 class MainHandler(tornado.web.RequestHandler):
   2     def get(self):
   3         if not self.get_secure_cookie("mycookie"):
   4             self.set_secure_cookie("mycookie", "myvalue")
   5             self.write("Your cookie was not set yet!")
   6         else:
   7             self.write("Your cookie was set!")

Аутентификация пользователей

Защита от CSRF

Статические файлы и агрессивное кеширование файлов

Локализация

Модули пользовательского интерфейса

Для облегчения поддержки стандартных и многократно используемых виджетов вашими приложениями, в торнадо включена поддержка модулей пользовательского интерфейса (UI Modules). По сути, они являются особыми функциональными запросами на отображение компонентов вашей страницы и они идут в комплекте с собственными таблицами CSS и функциями JavaScript.

Допустим, что у нас есть блог и мы хотим, чтобы записи этого блога присутствовали как на главной странице, так и на отдельных страницах записей. Для этого вы можете сделать класс Entry, унаследовав его от tornado.web.UIModule, вызывая который вы сможете отображать модуль записи на любой типе страницы. Для начала, создайте отдельный файл python в котором мы будем хранить модули интерфейса: uimodules.py и разместите в него следующий код:

   1 class Entry(tornado.web.UIModule):
   2     def render(self, entry, show_comments=False):
   3         return self.render_string(
   4             "module-entry.html", show_comments=show_comments)

В настройках приложения укажите uimodules.py в качестве файла с модулями интерфейса, используя опцию ui_modules.

   1 class HomeHandler(tornado.web.RequestHandler):
   2     def get(self):
   3         entries = self.db.query("SELECT * FROM entries ORDER BY date DESC")
   4         self.render("home.html", entries=entries)
   5 
   6 class EntryHandler(tornado.web.RequestHandler):
   7     def get(self, entry_id):
   8         entry = self.db.get("SELECT * FROM entries WHERE id = %s", entry_id)
   9         if not entry: raise tornado.web.HTTPError(404)
  10         self.render("entry.html", entry=entry)
  11 
  12 settings = {
  13     "ui_modules": uimodules,
  14 }
  15 application = tornado.web.Application([
  16     (r"/", HomeHandler),
  17     (r"/entry/([0-9]+)", EntryHandler),
  18 ], **settings)

В основном шаблоне home.html вы не включаете html-код напрямую, а ссылаетесь на модуль Entry

   1 {% for entry in entries %}
   2   {{ modules.Entry(entry) }}
   3 {% end %}

В шаблоне отдельной записи entry.html вы также ссылаетесь на модуль Entry, но уже с аргументом show_comments , чтобы показать расширенный вариант модуля записи:

   1 {{ modules.Entry(entry, show_comments=True) }}

В модули могут включаться произвольные CSS таблицы стилей и функции JavaScript посредством переопределеня методов embedded_css, embedded_javascript, javascript_files или css_files.

   1 class Entry(tornado.web.UIModule):
   2     def embedded_css(self):
   3         return ".entry { margin-bottom: 1em; }"
   4 
   5     def render(self, entry, show_comments=False):
   6         return self.render_string(
   7             "module-entry.html", show_comments=show_comments)

Модули CSS и JavaScript будут подключены к итоговой странице единократно, вне зависимости от количества вызовов модуля пользовательского интерфейса в пределах нее. CSS будет включена в элемент <head> страницы, а JavaScript будет размещен прямо перед тегом </body> в конце страницы.

Неблокирующие, асинхронные запросы

Обычно, когда заканчивается выполнение обработчика HTTP запроса, запрос автоматически завершается. Так как Tornado использует неблокирующий стиль ввода-вывода, но вы можете переназначить стандартное поведение, если хотите чтобы запрос оставался открытым после возврата из основного метода обработчика, используя декоратор класса-обработчика tornado.web.asynchronous.

При его использовании вы должны сами вызвать self.finish(), чтобы завершить HTTP запрос, или вы создадите лишнюю нагрузку на браузер пользователя или "повесите" его.

   1 class MainHandler(tornado.web.RequestHandler):
   2     @tornado.web.asynchronous
   3     def get(self):
   4         self.write("Hello, world")
   5         self.finish()

Ниже приведен рабочий пример, который делает обращение к FriendFeed API, используя встроенный асинхронный HTTP клиент Торнадо:

   1 class MainHandler(tornado.web.RequestHandler):
   2     @tornado.web.asynchronous
   3     def get(self):
   4         http = tornado.httpclient.AsyncHTTPClient()
   5         http.fetch("http://friendfeed-api.com/v2/feed/bret",
   6                    callback=self.async_callback(self.on_response))
   7 
   8     def on_response(self, response):
   9         if response.error: raise tornado.web.HTTPError(500)
  10         json = tornado.escape.json_decode(response.body)
  11         self.write("Fetched " + str(len(json["entries"])) + " entries "
  12                    "from the FriendFeed API")
  13         self.finish()

Когда метод get() возвращает управление, HTTP запрос остается незавершенным. Когда, наконец, вызывается метод on_responce(), запрос все еще открыт. В конце метода ответа мы должны отдать браузеру результат выполнения обработчика и закрыть соединение посредством вызова self.finish().

Если вы вызываете асинхронные библиотечные функции, которые требуют ответа (например функция HTTP fetch в примере выше), вы должны обязательно обернуть их self.async_callback. Эта простая обертка гарантирует, что если ваша функция создаст исключение или программную ошибку, то браузеру будет послан соответствующий HTTP-ответ с кодом ошибки и соединение будет корректно завершено.

В качестве более развернутого примера вы можете посмотреть приложение chat, идущее в качестве примера с Tornado, которое является чатом на основе AJAX и использует т.н. длинные запросы (long polling)

Аутентификация от третих разработчиков

Производительность

Запуск Tornado на производстве

WSGI и Google AppEngine

Предостережения и поддержка

Перевод: Ростислав Дзинько, Илья Кутуков

Документации/Tornado-web (последним исправлял пользователь RostislavDzinko 2010-07-26 17:37:55)