Spec-Zone.ru › Falcon 2.0

ЧаВО

  • Философия проектирования
    • Почему Falcon не поставляется с батарейками?
    • Почему Falcon не создаёт новый экземпляр Resource для каждого запроса?
    • Почему возникновение ошибки внутри ресурса приводит к сбою приложения?
    • Как сгенерировать документацию API для моего Falcon API?
  • Производительность
    • Поддерживает ли Falcon протокол HTTP/2?
    • Falcon потокобезопасен?
    • Falcon поддерживает asyncio?
    • Falcon поддерживает WebSocket?
  • Маршрутизация
    • Как реализовать CORS с помощью Falcon?
    • Как реализовать перенаправления внутри Falcon?
    • Как разделить запросы между моим исходным приложением и частью, перенесённой в Falcon?
    • Как реализовать как POST, так и GET операции для одного ресурса?
    • Какой рекомендуемый способ сопоставления связанных маршрутов с классами ресурсов?
  • Расширяемость
    • Как использовать WSGI-прослойки с Falcon?
    • Как передать данные от обработчика к отвечающему объекту и между обработчиками?
    • Как написать пользовательскую обработку страниц 404 и 500 в Falcon?
  • Обработка запросов
    • Как аутентифицировать запросы?
    • Почему req.stream.read() зависает для определённых запросов?
    • Как Falcon обрабатывает конечный слэш в пути запроса?
    • Почему мой параметр запроса отсутствует в объекте req?
    • Почему символы '+' в моих параметрах преобразуются в пробелы?
    • Как получить доступ к отправленным параметрам формы?
    • Как получить доступ к отправленным файлам?
    • Как обработать строку запроса с JSON-значением?
    • Как обработать слэши в поле шаблона маршрута?
    • Как адаптировать код к изменениям типов контекста по умолчанию в Falcon 2.0?
  • Обработка ответов
    • Как использовать resp.media с типами, такими как datetime?
    • Falcon устанавливает Content-Length, или нужно делать это явно?
    • Почему возвращается пустое тело ответа, когда я генерирую исключение типа HTTPError?
    • Установил тело ответа, но оно не возвращается. Что происходит?
    • Установил cookie, но он не возвращается в последующих запросах.
    • Как отдать файл для скачивания с помощью Falcon?
    • Falcon может отдавать статические файлы?
  • Разное
    • Как управлять соединениями с базой данных?
    • Какой рекомендуемый подход для доступа к переменным конфигурации из нескольких классов ресурсов?
    • Как протестировать приложение Falcon? Можно ли использовать pytest?

Философия проектирования

Почему Falcon не поставляется с батарейками?

Falcon разработан для приложений, требующих высокой степени настройки или регулировки производительности. Минималистский дизайн фреймворка освобождает разработчика от выбора лучших стратегий и сторонних пакетов для задачи.

Экосистема Python предлагает множество отличных пакетов, которые вы можете использовать в своих обработчиках, крючках и компонентах прослойки. В качестве отправной точки сообщество поддерживает список дополнений и дополнительных пакетов Falcon.

Почему Falcon не создаёт новый экземпляр Resource для каждого запроса?

Falcon обычно старается свести к минимуму количество создаваемых объектов. Это делается по двум причинам: во-первых, для экономии расходов на создание объекта, и во-вторых, для снижения использования памяти путём уменьшения общего количества объектов при высоких нагрузках. Поэтому при добавлении маршрута Falcon требует экземпляр вашего класса ресурса, а не тип класса. Тот же экземпляр будет использоваться для обработки всех поступающих запросов по этому маршруту.

Почему возникновение ошибки внутри ресурса приводит к сбою приложения?

Как правило, Falcon предполагает, что обработчики ресурсов (например, on_get(), on_post(), и т.д.) будут, в большинстве случаев, действовать правильно. Другими словами, Falcon не сильно старается защитить код обработчиков от самих себя.

Этот подход уменьшает количество проверок, которые Falcon в противном случае должен был бы выполнять, что делает фреймворк более эффективным. С учётом этого, создание качественного API на основе Falcon требует:

  1. Обработчики ресурсов устанавливают переменные ответа в разумные значения.
  2. Ваш код хорошо протестирован с высокой степенью покрытия кода.
  3. Ошибки предсказываются, обнаруживаются и обрабатываются надлежащим образом внутри каждого обработчика и с помощью пользовательских обработчиков ошибок.

Подсказка

Falcon будет повторно генерировать исключения, которые не наследуются от HTTPError, если вы не зарегистрировали пользовательский обработчик ошибок для этого типа (см. также: falcon.API).

Как сгенерировать документацию API для моего Falcon API?

В отношении документации API некоторые разработчики предпочитают использовать реализацию API в качестве пользовательского контракта или источника достоверной информации (подход «реализация-сначала»), а другие предпочитают использовать сам API-спецификацию в качестве контракта, реализуя и тестируя API на основе этой спецификации (подход «дизайн-сначала»).

Рискуя переборщить с гибкостью, Falcon не предоставляет поддержку API-спецификаций в стандартной поставке. Однако в сообществе есть несколько проектов в этом направлении. Наш каталог дополнений перечисляет несколько таких проектов, а также можно поискать на PyPI дополнительные пакеты.

Если вы заинтересованы в подходе «дизайн-сначала», упомянутом выше, вы также можете изучить проектирование API и шлюзы API, такие как Tyk, Apiary, Amazon API Gateway или Google Cloud Endpoints.

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

Поддерживает ли Falcon протокол HTTP/2?

Falcon — это фреймворк WSGI, и как таковой он не обрабатывает HTTP-запросы напрямую. Тем не менее, вы можете получить большую часть преимуществ HTTP/2, просто развернув любой совместимый с HTTP/2 веб-сервер или балансировщик нагрузки перед вашим приложением, чтобы он переводил между HTTP/2 и HTTP/1.1. В конечном итоге мы ожидаем, что Python веб-серверы (такие как uWSGI) будут поддерживать HTTP/2 напрямую, что устранит необходимость слоя преобразования.

Falcon потокобезопасен?

Фреймворк Falcon, сам по себе, потокобезопасен. Например, новые объекты Request и Response создаются для каждого входящего HTTP-запроса. Однако, один экземпляр каждого класса ресурса, привязанного к маршруту, используется совместно для всех запросов. Объекты middleware и другие типы хуков, такие как обработчики пользовательских ошибок, также используются совместно. Таким образом, пока вы реализуете эти классы и вызываемые объекты потокобезопасным способом и убедитесь, что любые сторонние библиотеки, используемые вашим приложением, также потокобезопасны, ваше WSGI-приложение в целом будет потокобезопасным.

Тем не менее, IO-связанные API Falcon обычно масштабируются с помощью нескольких процессов и зелёных потоков (с помощью библиотеки gevent или аналогичной), которые фактически не работают одновременно, поэтому могут быть некоторые граничные случаи, в которых Falcon не потокобезопасен, о которых нам неизвестно. Если у вас возникнут какие-либо проблемы, сообщите нам об этом.

Поддерживает ли Falcon asyncio?

Из-за ограничений WSGI, Falcon не может поддерживать asyncio в настоящее время. Однако мы изучаем альтернативы WSGI (такие как ASGI), которые позволят нам в будущем поддерживать asyncio напрямую.

Тем временем, мы рекомендуем использовать проверенную библиотеку gevent через Gunicorn или uWSGI для масштабирования IO-связанных сервисов. meinheld также успешно использовался сообществом для создания высокопроизводительных сервисов с низкой задержкой. Обратите внимание, что если вы используете Gunicorn, вы можете комбинировать gevent и PyPy для достижения впечатляющего уровня производительности. (К сожалению, uWSGI пока не поддерживает совместное использование gevent и PyPy.)

Поддерживает ли Falcon WebSocket?

Из-за ограничений WSGI Falcon не может поддерживать протокол WebSocket, как указано выше.

В то же время, вы можете попробовать использовать родную поддержку WebSocket в uWSGI или реализовать автономный сервис с помощью удобной библиотеки Aymeric Augustin’s websockets.

Маршрутизация

Как реализовать CORS с Falcon?

Для того, чтобы веб-сайт или SPA могли получить доступ к API, размещённому на другом домене, этот API должен реализовать Cross-Origin Resource Sharing (CORS). Для публичного API реализация CORS в Falcon может быть такой же простой, как реализация компонента middleware, подобного следующему:

class CORSComponent(object):
    def process_response(self, req, resp, resource, req_succeeded):
        resp.set_header('Access-Control-Allow-Origin', '*')

        if (req_succeeded
            and req.method == 'OPTIONS'
            and req.get_header('Access-Control-Request-Method')
        ):
            # NOTE(kgriffs): This is a CORS preflight request. Patch the
            #   response accordingly.

            allow = resp.get_header('Allow')
            resp.delete_header('Allow')

            allow_headers = req.get_header(
                'Access-Control-Request-Headers',
                default='*'
            )

            resp.set_headers((
                ('Access-Control-Allow-Methods', allow),
                ('Access-Control-Allow-Headers', allow_headers),
                ('Access-Control-Max-Age', '86400'),  # 24 hours
            ))

При использовании вышеприведённого подхода запросы OPTIONS также должны обрабатываться особо во всех других middleware или хуках, которые вы используете для аутентификации, определения типа контента и т. д. Например, вы обычно пропускаете аутентификацию для предварительных запросов, потому что она просто не нужна; обратите внимание, что такие запросы не включают заголовок Authorization в любом случае.

Для более сложных случаев взгляните на дополнения Falcon от сообщества, такие как falcon-cors, или попробуйте одну из общих библиотек WSGI CORS, доступных на PyPI . Если вы используете шлюз API, вы также можете посмотреть на функциональность CORS, которую он предоставляет на этом уровне.

Как реализовать перенаправления в Falcon?

Falcon предоставляет ряд классов исключений, которые могут быть подняты для перенаправления клиента на другое место (см. также Перенаправление).

Однако, если возможно, более эффективно обрабатывать постоянные перенаправления непосредственно с помощью вашего веб-сервера, а не добавлять дополнительную нагрузку на ваше приложение для таких запросов.

Как разделить запросы между моим оригинальным приложением и частью, которую я перенес в Falcon?

Часто бывает целесообразно выделить часть приложения и переписать её на Falcon, чтобы повысить производительность там, где это наиболее необходимо.

Если у вас есть доступ к настройкам вашего балансировщика нагрузки или обратного прокси, мы рекомендуем установить правила на основе пути или поддомена, чтобы разделить запросы между вашей оригинальной реализацией и частями, которые были перенесены в Falcon (например, добавив дополнительную location директиву в вашу конфигурацию NGINX).

Если вышеуказанный подход не подходит для вашей среды развертывания, вы можете реализовать простой WSGI-обёртку, которая выполняет то же самое:

def application(environ, start_response):
    try:
        # NOTE(kgriffs): Prefer the host header; the web server
        # isn't supposed to mess with it, so it should be what
        # the client actually sent.
        host = environ['HTTP_HOST']
    except KeyError:
        # NOTE(kgriffs): According to PEP-3333, this header
        # will always be present.
        host = environ['SERVER_NAME']

    if host.startswith('api.'):
        return falcon_app(environ, start_response)
    elif:
        return webapp2_app(environ, start_response)

См. также PEP 3333 для полного списка переменных, предоставляемых через environ.

Как реализовать как POST, так и GET для одного ресурса?

Предположим, у вас есть следующие маршруты:

# Resource Collection
GET /resources{?marker, limit}
POST /resources

# Resource Item
GET /resources/{id}
PATCH /resources/{id}
DELETE /resources/{id}

Вы можете реализовать такой API, просто используя два класса Python, один для представления одного ресурса, а другой для представления коллекции этих ресурсов. Обычно оба класса размещаются в одном модуле (см. также эту часть учебника).

В качестве альтернативы, вы можете использовать суффиксные обработчики для сопоставления обоих маршрутов с одним классом ресурса:

class MyResource(object):
    def on_get(self, req, resp, id):
        pass

    def on_patch(self, req, resp, id):
        pass

    def on_delete(self, req, resp, id):
        pass

    def on_get_collection(self, req, resp):
        pass

    def on_post_collection(self, req, resp):
        pass


# ...


resource = MyResource()
api.add_route('/resources/{id}', resource)
api.add_route('/resources', resource, suffix='collection')

Какой рекомендуемый способ сопоставить связанные маршруты с классами ресурсов?

Допустим, у нас есть следующая схема URL:

GET  /game/ping
GET  /game/{game_id}
POST /game/{game_id}
GET  /game/{game_id}/state
POST /game/{game_id}/state

Мы можем разбить это на три ресурса:

Ping:

    GET  /game/ping

Game:

    GET  /game/{game_id}
    POST /game/{game_id}

GameState:

    GET  /game/{game_id}/state
    POST /game/{game_id}/state

GameState можно рассматривать как подресурс Game. Это отдельная логическая сущность, вложенная в более общий концепт Game.

В Falcon эти ресурсы будут реализованы стандартными классами:

class Ping(object):

    def on_get(self, req, resp):
        resp.body = '{"message": "pong"}'


class Game(object):

    def __init__(self, dao):
        self._dao = dao

    def on_get(self, req, resp, game_id):
        pass

    def on_post(self, req, resp, game_id):
        pass


class GameState(object):

    def __init__(self, dao):
        self._dao = dao

    def on_get(self, req, resp, game_id):
        pass

    def on_post(self, req, resp, game_id):
        pass


api = falcon.API()

# Game and GameState are closely related, and so it
# probably makes sense for them to share an object
# in the Data Access Layer. This could just as
# easily use a DB object or ORM layer.
#
# Note how the resources classes provide a layer
# of abstraction or indirection which makes your
# app more flexible since the data layer can
# evolve somewhat independently from the presentation
# layer.
game_dao = myapp.DAL.Game(myconfig)

api.add_route('/game/ping', Ping())
api.add_route('/game/{game_id}', Game(game_dao))
api.add_route('/game/{game_id}/state', GameState(game_dao))

В качестве альтернативы, один класс ресурса может реализовать суффиксные обработчики для обработки всех трёх маршрутов:

class Game(object):

    def __init__(self, dao):
        self._dao = dao

    def on_get(self, req, resp, game_id):
        pass

    def on_post(self, req, resp, game_id):
        pass

    def on_get_state(self, req, resp, game_id):
        pass

    def on_post_state(self, req, resp, game_id):
        pass

    def on_get_ping(self, req, resp):
        resp.data = b'{"message": "pong"}'


# ...


api = falcon.API()

game = Game(myapp.DAL.Game(myconfig))

api.add_route('/game/{game_id}', game)
api.add_route('/game/{game_id}/state', game, suffix='state')
api.add_route('/game/ping', game, suffix='ping')

Расширяемость

Как использовать WSGI middleware с Falcon?

Экземпляры falcon.API являются полноценными WSGI-приложениями, поэтому вы можете использовать стандартный шаблон, описанный в PEP-3333. В вашем основном файле «app» вы просто обернёте ваш экземпляр API приложением middleware. Например:

import my_restful_service
import some_middleware

app = some_middleware.DoSomethingFancy(my_restful_service.api)

См. также пример WSGI middleware, приведённый в PEP-3333.

Как передать данные из хука в обработчик и между хуками?

Вы можете ввести дополнительные значения kwargs обработчика из хука, добавив их в словарь params, переданный в хук. Также можно установить пользовательские атрибуты объекта req.context, как способ передачи контекстной информации:

def authorize(req, resp, resource, params):
    # Check authentication/authorization
    # ...

    req.context.role = 'root'
    req.context.scopes = ('storage', 'things')
    req.context.uid = 0

# ...

@falcon.before(authorize)
def on_post(self, req, resp):
    pass

Как написать пользовательский обработчик для страниц 404 и 500 в Falcon?

Когда маршрут не найден для входящего запроса, Falcon использует по умолчанию обработчик, который просто поднимает экземпляр falcon.HTTPNotFound. Вы можете использовать falcon.API.add_error_handler() для регистрации пользовательского обработчика ошибок для этого типа исключения. В качестве альтернативы, вы можете настроить свой веб-сервер для преобразования ответа (например, с помощью директивы Nginx’s error_page).

Ошибки 500 обычно являются результатом необработанного исключения, которое достигло веб-сервера. Чтобы более элегантно обрабатывать эти ошибки, вы можете добавить пользовательский обработчик ошибок для базового типа Python Exception.

Обработка запросов

Как аутентифицировать запросы?

Для аутентификации и авторизации запросов можно использовать вместе хуки и компоненты middleware. Например, компонент middleware можно использовать для разбора входящих учетных данных и размещения результатов в req.context. Затем последующие компоненты или хуки могут использовать эту информацию для авторизации запроса, учитывая роль пользователя и запрашиваемый ресурс.

Почему req.stream.read() зависает для определённых запросов?

Это поведение — неприятное следствие того, что механика тела запроса не полностью определена спецификацией WSGI (PEP-3333). Это обсуждается в справочной документации для stream, и обходной путь представлен в виде bounded_stream.

Как Falcon обрабатывает слеш в конце пути запроса?

Если ваше приложение устанавливает strip_url_path_trailing_slash в True, Falcon будет нормализовать пути URI входящих запросов, чтобы упростить последующую обработку и повысить предсказуемость логики приложения. Это может быть полезно при реализации схемы REST API, которая не интерпретирует символ слеша в конце пути как ссылку на неявный подресурс, как это традиционно используется веб-сайтами для ссылки на страницы индекса.

Например, при включении этого параметра добавление маршрута для '/foo/bar' неявно добавляет маршрут для '/foo/bar/'. Другими словами, запросы, поступающие для любого из этих путей, будут отправлены в один и тот же ресурс.

Примечание

Начиная с версии 2.0, значение по умолчанию для параметра запроса strip_url_path_trailing_slash изменилось с True на False.

Почему мой параметр запроса отсутствует в объекте req?

Если параметр запроса не имеет значения, Falcon по умолчанию игнорирует этот параметр. Например, передача 'foo' или 'foo=' приведёт к игнорированию параметра.

Если вы хотите распознать такие параметры, вы должны установить параметр запроса keep_blank_qs_values в значение True. Параметры запроса устанавливаются глобально для каждого экземпляра falcon.API через свойство req_options. Например:

api.req_options.keep_blank_qs_values = True

Почему символы ‘+’ в моих параметрах преобразуются в пробелы?

Символ + часто используется вместо %20 для представления пробелов в параметрах строки запроса из-за исторической связи кодирования параметров формы (application/x-www-form-urlencoded) и процентов кодирования URI. Поэтому Falcon преобразует + в пробел при декодировании строк.

Для решения этой проблемы RFC 3986 определяет + как зарезервированный символ и рекомендует процентов кодирование таких символов, когда требуется их буквальное значение (%2B в случае +).

Как получить доступ к параметрам POST-запроса формы?

По умолчанию Falcon не обрабатывает тела запроса. Однако, установив значение auto_parse_form_urlencoded на True в экземпляре falcon.API, фреймворк будет обрабатывать тело запроса, когда тип контента равен application/x-www-form-urlencoded, делая параметры формы доступными через params, get_param() и т.д.

api.req_options.auto_parse_form_urlencoded = True

В качестве альтернативы, параметры POST-запроса формы могут быть прочитаны непосредственно из stream и обработаны с помощью falcon.uri.parse_query_string() или urllib.parse.parse_qs().

Как получить доступ к загруженным файлам?

В настоящее время Falcon не поддерживает разбор файлов, отправленных с помощью HTTP-формы (multipart/form-data), хотя мы планируем добавить эту функцию в будущей версии. Тем временем, вы можете использовать стандартный класс cgi.FieldStorage для разбора запроса:

# TODO: Either validate that content type is multipart/form-data
# here, or in another hook before allowing execution to proceed.

# This must be done to avoid a bug in cgi.FieldStorage
env = req.env
env.setdefault('QUERY_STRING', '')

# TODO: Add error handling, when the request is not formatted
# correctly or does not contain the desired field...

# TODO: Consider overriding make_file, so that you can
# stream directly to the destination rather than
# buffering using TemporaryFile (see http://goo.gl/Yo8h3P)
form = cgi.FieldStorage(fp=req.stream, environ=env)

file_item = form[name]
if file_item.file:
    # TODO: It's an uploaded file... read it in
else:
    # TODO: Raise an error

Вы также можете попробовать пакет streaming_form_data от Siddhant Goel или поискать на PyPI дополнительные варианты от сообщества.

Как обработать строку запроса со значением JSON?

Falcon по умолчанию рассматривает запятые в строке запроса как буквальные символы, разделяющие список, разделенный запятыми. Например, при строке запроса ?c=1,2,3, Falcon по умолчанию добавляет это в ваш словарь request.params как {'c': ['1', '2', '3']}. Если вы попытаетесь использовать JSON в значении строки запроса, например ?c={'a':1,'b':2}, значение будет добавлено в ваш словарь request.params не так, как ожидается: {'c': ["{'a':1", "'b':2}"]}.

Запятая — зарезервированный символ, который можно экранировать в соответствии с RFC 3986 - 2.2. Зарезервированные символы, поэтому одним из возможных решений является процентов кодирование всех запятых, присутствующих в строке запроса JSON. Другой вариант — изменить способ обработки запятых Falcon в строке запроса, установив значение auto_parse_qs_csv на False в экземпляре falcon.API:

api.req_options.auto_parse_qs_csv = False

Когда auto_parse_qs_csv установлено на False, значение строки запроса ?c={'a':1,'b':2} будет добавлено в словарь req.params как {'c': "{'a':1,'b':2}"}. Это позволяет обрабатывать JSON, независимо от того, выбрал ли клиент экранировать запятые в запросе.

Как обработать обратные слэши в поле шаблона маршрута?

В Falcon 1.3 мы добавили начальную поддержку преобразователей полей. Мы обсуждали расширение этой возможности для обработки нескольких сегментов пути, подобно Flask. Эта работа запланирована на 2.0.

Временное решение — процентов кодирование обратного слэша. Если вы не контролируете клиентов и не можете это обеспечить, вы можете реализовать компонент middleware Falcon для переписывания пути перед маршрутизацией.

Как адаптировать код к изменениям типа контекста по умолчанию в Falcon 2.0?

По умолчанию тип контекста запроса/ответа изменён с dict на пустой класс в Falcon 2.0. Вместо установки элементов словаря, теперь вы можете просто установить атрибуты в объекте:

# Before Falcon 2.0
req.context['cache_backend'] = MyUltraFastCache.connect()

# Falcon 2.0
req.context.cache_backend = MyUltraFastCache.connect()

Новый тип контекста по умолчанию эмулирует интерфейс отображения словаря таким образом, что атрибуты контекста связаны с элементами словаря. То есть установка атрибута объекта также устанавливает соответствующий элемент словаря, и наоборот. В результате существующий код в основном будет работать без изменений с Falcon 2.0. Тем не менее, рекомендуется мигрировать на новый интерфейс, как описано выше, поскольку интерфейс отображения словаря может быть удален из типа контекста в будущей версии.

Предупреждение

Если вам нужно комбинировать оба подхода при миграции, имейте в виду, что установка таких атрибутов, как items или values, очевидно, перекроет соответствующие функции интерфейса отображения.

Если существующий проект активно использует контексты словарей, тип можно явно переопределить обратно на dict, используя пользовательские типы запроса/ответа:

class RequestWithDictContext(falcon.Request):
    context_type = dict

class ResponseWithDictContext(falcon.Response):
    context_type = dict

# ...

api = falcon.API(request_type=RequestWithDictContext,
                 response_type=ResponseWithDictContext)

Обработка ответов

Как использовать resp.media с типами, такими как datetime?

Обработчик JSON по умолчанию для resp.media поддерживает только объекты и типы, перечисленные в таблице, описанной в json.JSONEncoder. Для обработки дополнительных типов вы можете либо предварительно сериализовать их, либо создать пользовательский обработчик JSON media, который задаёт параметр default для json.dumps(). При десериализации входящего тела запроса вы также можете реализовать object_hook для json.loads(). Однако обратите внимание, что установка параметров default или object_hook может негативно повлиять на производительность (де)сериализации.

Falcon устанавливает Content-Length или мне нужно сделать это явно?

Falcon попытается сделать это за вас, основываясь на значении resp.body, resp.data, или resp.stream_len (которое установлено в ответе, проверяется в этом порядке).

Для динамически генерируемого содержимого вы можете не устанавливать stream_len, в этом случае Falcon не будет включать заголовок Content-Length, и, надеюсь, ваш сервер WSGI сделает правильные выводы™ (предполагается, что вы сказали ему включить keep-alive).

Примечание

PEP-3333 запрещает приложениям устанавливать самим заголовки hop-by-hop, такие как Transfer-Encoding.

Почему возвращается пустое тело ответа, когда я поднимаю экземпляр HTTPError?

Falcon пытается сериализовать экземпляр HTTPError с использованием методов to_json() или to_xml() в соответствии с заголовком Accept в запросе. Если ни JSON, ни XML не допустимы, тело ответа не будет сгенерировано. Вы можете переопределить это поведение при необходимости, используя set_error_serializer().

Я устанавливаю тело ответа, но оно не возвращается. В чём дело?

Falcon пропускает обработку тела ответа, когда, согласно спецификации HTTP, тело не должно возвращаться. Если клиент отправляет запрос HEAD, фреймворк всегда вернёт пустое тело. Falcon также вернёт пустое тело, когда код ответа — любой из следующих:

falcon.HTTP_100
falcon.HTTP_204
falcon.HTTP_416
falcon.HTTP_304

Если у вас есть другой случай, когда тело не возвращается, это, вероятно, ошибка! Сообщите нам, чтобы мы могли помочь.

Я устанавливаю cookie, но он не возвращается в последующих запросах.

По умолчанию Falcon включает атрибут cookie secure. Поэтому, если вы тестируете ваше приложение через HTTP (а не HTTPS), клиент не отправит cookie в последующих запросах.

(См. также документацию по cookie.)

Как я могу предоставить скачиваемый файл с falcon?

В методе on_get() ответа для ресурса, вы можете сообщить агенту пользователя, чтобы он скачал файл, установив заголовок Content-Disposition. Falcon включает свойство downloadable_as для упрощения этой задачи:

resp.downloadable_as = 'report.pdf'

Falcon может предоставлять статические файлы?

Falcon упрощает эффективное предоставление статических файлов, просто назначив открытый файл свойству resp.stream как показано в руководстве. Вы также можете предоставить всю папку файлов с помощью falcon.API.add_static_route(). Однако, если возможно, лучше предоставлять статические файлы непосредственно с веб-сервера, такого как Nginx, или с CDN.

Разное

Как управлять подключениями к базе данных?

Предполагая, что ваша библиотека базы данных управляет собственным пулом подключений, всё, что вам нужно сделать, это инициализировать клиента и передать его экземпляр в ваши классы ресурсов. Например, используя SQLAlchemy Core:

engine = create_engine('sqlite:///:memory:')
resource = SomeResource(engine)

Затем, внутри SomeResource:

# Read from the DB
result = self._engine.execute(some_table.select())
for row in result:
    # ....
result.close()

# ...

# Write to the DB within a transaction
with self._engine.begin() as connection:
    r1 = connection.execute(some_table.select())
    # ...
    connection.execute(
        some_table.insert(),
        col1=7,
        col2='this is some data'
    )

При использовании уровня доступа к данным, просто передайте движок в свои объекты доступа к данным. См. также этот пример проекта Falcon, который демонстрирует использование ORM с Falcon.

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

Если вам нужно прозрачно обрабатывать повторное подключение после ошибки или для других случаев использования, которые могут не поддерживаться вашей библиотекой клиента, просто инкапсулируйте библиотеку клиента в классе управления, который обрабатывает все сложные моменты, и передавайте его вместо этого.

Какой рекомендуемый подход для обеспечения доступа к переменным конфигурации для нескольких классов ресурсов?

Люди обычно делятся на два лагеря, когда речь заходит об этом вопросе. Первый лагерь предпочитает создавать объект конфигурации и передавать его инициализаторам классов ресурсов, чтобы разделение данных было явным. Второй лагерь предпочитает создавать модуль конфигурации и импортировать его там, где это необходимо.

С последним подходом, чтобы контролировать, когда конфигурация фактически загружается, лучше не инициализировать её на верхнем уровне пространства имён модуля конфигурации. Это предотвращает любые проблемные побочные эффекты, которые могут быть вызваны загрузкой конфигурации всякий раз, когда Python обрабатывает первый импорт модуля конфигурации. Вместо этого рассмотрите возможность реализации функции в модуле, которая возвращает новый или кэшированный объект конфигурации по запросу.

Помимо этого, вы сами решаете, хотите ли вы использовать библиотеку конфигурации стандартной библиотеки или что-то вроде aumbry, как показано в этом примере приложения Falcon.

(См. также раздел **Конфигурация** нашей страницы вики-помощных пакетов. Вы также можете поискать другие варианты на PyPI).

Как протестировать моё приложение Falcon? Могу ли я использовать pytest?

Фреймворк для тестирования Falcon поддерживает как unittest, так и pytest. На самом деле, учебник в документации предоставляет отличное введение в тестирование приложений Falcon с помощью pytest.

(См. также: Тестирование)

© 2019 by Falcon contributors
Licensed under the Apache License, Version 2.0.
https://falcon.readthedocs.io/en/2.0.0/user/faq.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API