Руководство по Python Bottle (Перевод) Часть 3 - Маршрутизация запросов

В предыдущей части (Руководство по Python Bottle (Перевод) Часть 2 — Hello World) мы создали простое Web приложение, с одним единственным маршрутом, вот та часть кода с маршрутизацией из примера «Hello WOrld»:

@route('/hello')
def hello():
    return "Hello World!"

В этом примере декоратор route() связывает URL путь с вызываемой функцией, и добавляет новый маршрут.

Теперь давайте добавим ещё один маршрут:

@route('/')
@route('/hello/<name>')
def greet(name='Незнакомец'):
    return template('Здравствуй {{name}}, как дела?', name=name)

(Не забываем ипортировать необходимые библиотеки: from bottle import template)

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


Динамическая маршрутизация


Маршруты которые содержат шаблоны (шаблоны маршрутизации), называются динамическими и соответствуют сразу многим URL-адресам одновременно. Простой шаблон состоит из имени, заключенного в угловые скобки,
(например, <name>)
и принимает один или несколько символов до следующей косой черты (/). К примеру, маршрут
/hello/<name>
принимает запросы для /hello/roman, а также /hello/lena , но не принимает для /hello , /hello/ или /hello/mr/roman .

Каждый шаблон берет соответствующую часть URL запроса и в качестве аргумента ключевого слова передает в вызываемую функцию. Это можно легко использовать и внедрить RESTful (чистые ссылки), красивые и понятные URL-адреса. Вот ещё несколько примеров маршрутов с соответствующими им URL-адресами:
@route('/wiki/<pagename>')            # matches /wiki/Learning_Python
def show_wiki_page(pagename):
    ...

@route('/<action>/<user>')            # matches /follow/defnull
def user_api(action, user):
    ...


Для более точного определения значения в части URL адреса представленой шаблоном и передачи или преобразования значения в вызываемую функцию могут быть использованы фильтры. Шаблон с фильтром записывается следующим образом:
<name:filter>
или
<name:filter:config>
Часть config — опциональная и её синтаксис зависит от используемого фильтра.

Приведенные ниже фильтры включены по умолчанию:
: int целое число со знаком (со знаком) — преобразует значение в целое число.
: float аналогично: int, но для десятичных чисел.
: path соответствует всем символам, включая символ косой черты, и может использоваться для соответствия нескольким сегментам пути.
: re позволяет вам указать пользовательское регулярное выражение в поле конфигурации.

Посмотрим некоторые практические примеры:
@route('/object/<id:int>')
def callback(id):
    assert isinstance(id, int)

@route('/show/<name:re:[a-z]+>')
def callback(name):
    assert name.isalpha()

@route('/static/<path:path>')
def callback(path):
    return static_file(path, ...)


Методы HTTP запросов (HTTP REQUEST METHODS)


HTTP протокол определяет несколько методов для различных задач. Метод GET является методом по умолчанию, для всех маршрутов для которых не метод явно не определён, такие маршруты будут соответствовать только запросам методом GET. Для обработки других методов, таких как POST, PUT, DELETE или PATCH, необходимо добавить ключевое слово необходимого метода к декотратору route() или использовать один из пяти альтернативных декораторов: get(), post(), put(), delete() или patch().

Метод POST обычно применяется для отправки HTML форм. В примере ниже показано каким образом происходит обработка данных с формы авторизации в систему с помощью метода POST:

from bottle import get, post, request # or route

@get('/login') # or @route('/login')
def login():
    return '''
        <form action="/login" method="post">
            Username: <input name="username" type="text" />
            Password: <input name="password" type="password" />
            <input value="Login" type="submit" />
        </form>
    '''

@post('/login') # or @route('/login', method='POST')
def do_login():
    username = request.forms.get('username')
    password = request.forms.get('password')
    if check_login(username, password):
        return "<p>Вы ввели корректный логин и пароль.</p>"
    else:
        return "<p>Логин или пароль не верен.</p>"


В данном примере URL-адрес /login связан с двумя разными функциями: одна для запроса GET, другая для запроса POST. Первая отображает HTML форму для ввода информации пользователем. Вторая вызывается при отправке формы и проверяет учетные данные которые ввел пользователь.

Специальные методы: HEAD и ANY

Метод HEAD используется для получения ответа, идентичного запросу GET, но без тела ответа. Это полезно для получения мета-данных о ресурсе без необходимости загрузки всего документа. Bottle обрабатывает такие запросы автоматически, обращаясь к соответствующему маршруту GET и обрезая тело запроса, если оно есть. Нет необходимости описывать маршруты HEAD самостоятельно.

В дополнение, нестандартный метод ANY работает как запасной вариант с низким приоритетом: маршруты, которые прослушивает ANY, будут соответствовать запросам независимо от их метода HTTP, но только если не определен другой более точный маршрут. Это полезно для прокси-маршрутов, которые перенаправляют запросы в более конкретные подпрограммы.

Итого: запросы HEAD перенаправляются к маршрутам GET и возвращают только метаданные, а все остальные запросы возвращаются к маршрутам ANY, но только в том случае если не существует соответствующего маршрута для исходного метода запроса.

Маршрутизация статических файлов (ROUTING STATIC FILES)


Статические файлы, такие как изображения или файлы стилей CSS, автоматически не обслуживаются. Необходимо добавить маршрут и вызываемую функцию, для того чтобы контролировать, какие файлы будут обслуживаться и где их искать:

from bottle import static_file
@route('/static/<filename>')
def server_static(filename):
    return static_file(filename, root='/path/to/your/static/files')

Функция static_file() — помогает безопасного и удобного обслуживать файлы. Данный пример ограничен файлами непосредственно в каталоге /path/to/your/static/files, поскольку шаблон
<filename>
не будет совпадать с путем с косой чертой в нем. Чтобы обслуживать файлы в подкаталогах, необходимо изменить шаблон, чтобы использовать фильтр path:

@route('/static/<filepath:path>')
def server_static(filepath):
    return static_file(filepath, root='/path/to/your/static/files')

Будьте осторожны при указании относительного корневого пути, например root = '. / Static / files'. Рабочий каталог (./) и каталог проекта не всегда совпадают.

Страницы ошибок (ERROR PAGES)


Если что-то пошло не так, Bottle отобразит информативную, но довольно простую страницу с ошибкой. Однако можно переопределить значение по умолчанию для определенного кода состояния HTTP при помощи метода error():

from bottle import error
@error(404)
def error404(error):
    return 'Упс, извините страница не найдена!'

Теперь ошибки 404 File not Found будут отображать пользовательскую страницу ошибок. Единственный параметр, передаваемый обработчику ошибок, — это экземпляр HTTPError. Кроме того, обработчик ошибок очень похож на обычную функию запроса. Вы можете читать из запроса, писать в ответ и возвращать любой поддерживаемый тип данных, кроме экземпляров HTTPError.

Обработчики ошибок используются только в том случае, если ваше приложение возвращает или вызывает исключение HTTPError (abort() делает именно это). Изменение Request.status или возврат HTTPResponse не вызовет обработчик ошибок.

Продолжение:
Руководство по Python Bottle (Перевод) Часть 4 — Создание контента


0 комментариев

Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.