Python Falcon - 快速指南


Python Falcon - 简介

Falcon是一个用于开发关键任务 REST API 和微服务的 Python 库。它支持 WSGI 和 ASGI 规范。Falcon框架由Kurt Griffiths于2013年1月开发。Falcon的最新版本是3.1.0,于2022年3月发布。

Falcon 是一个轻量级的 Web 开发框架。其极简设计允许开发人员根据需要选择最佳策略和第三方软件包。

Falcon - 重要特性

Falcon 是根据 Apache 2.0 许可证条款发布的。

Falcon 的一些重要特征包括 -

  • 最新版本的 Falcon 支持 ASGI、WSGI 以及 WebSocket。

  • Falcon 为 asyncio 提供本机支持。

  • 其稳定的接口确保向后兼容性

  • Falcon 遵循 REST 架构风格来构建 API。

  • 基于类的 HTTP 资源构造。

  • 高度优化、可扩展的代码库。

  • Falcon 通过请求和响应类提供对标头和正文的轻松访问

  • 可用于 DRY 请求处理的中间件组件和挂钩。

  • 惯用的 HTTP 错误响应和异常处理。

Falcon-设计理念

Falcon 最小化实例化对象的数量,以避免创建对象的开销,并减少内存使用。同一实例将用于服务该路由上传入的所有请求。

  • 异常由资源响应者正确处理(例如on_get()、on_post()等方法)。Falcon 并没有尽力保护响应者代码免受自身侵害。高质量的 Falcon API 应满足以下要求 -

    • 资源响应者将响应变量设置为合理的值。

    • 您的代码经过充分测试,代码覆盖率很高。

    • 每个响应程序中都提供了自定义错误处理程序来预测、检测和处理错误。

  • Falcon 框架是线程安全的。为每个传入的 HTTP 请求创建单独的新 Request 和 Response 对象。但是,附加到路由的每个资源类的单个实例在所有请求之间共享。中间件对象、挂钩和自定义错误处理程序也是共享的。因此,您的 WSGI 应用程序作为一个整体将是线程安全的。

  • 从 3.0 版本开始,Falcon 支持asyncio。使用falcon.asgi.App类创建异步应用程序,并通过 ASGI 应用程序服务器(例如Uvicorn )为其提供服务。

  • Falcon 的异步版本支持 ASGI WebSocket协议。

Falcon - 与其他框架的比较

Python Web 框架主要分为两大类:全栈框架框架。

  • 全栈框架带有内置功能和库。Django、TurbogearsWeb2Py是全栈框架。

  • 相比之下,微框架是简约的,只提供最低限度的功能;因此,开发人员可以自由选择官方或第三方扩展,并且仅包含他们需要的插件。Flask、Falcon、Pyramid 都属于微框架范畴。

我们根据以下参数将 Falcon 框架与不同框架进行比较 -

表现

与 Flask、pyramid 等微框架相比,Falcon 应用速度非常快。全栈框架通常很慢。

休息支持

Falcon 旨在成为 REST API 和微服务开发的首选框架。FastAPI 还鼓励 REST 开发。Flask 和 Django 没有内置的 REST 支持。但是,可以使用扩展来启用它。

模板化

Falcon 应用程序不应提供模板网页。它不与任何模板库捆绑在一起。但是,可以使用jinja2Macho库。另一方面,Flask 内置了对jinja2的支持。Django 有自己的模板库。FastAPI 还可以处理任何选择的模板库。

数据库支持

Falcon 数据库支持不是内置的。可以使用 SQLAlchemy 模型与 MySQL、PostgreSQL、SQLite 等关系数据库进行交互。另一方面,Django 有自己的 ORM 框架,可以开箱即用。

Flask 应用程序还可以通过 Flask 扩展与数据库交互。TurboGears 的早期版本与 SQLObject ORM 库兼容。新版本与 SQLAlchemy 兼容。

灵活性

Falcon的应用非常灵活。它非常适合需要高度定制和性能调整的应用程序。FastAPI 和 Flask 的编码也很灵活,不会将用户限制在特定的项目或代码布局中。

安全

Falcon 没有内置支持来确保安全。Django 和 FastAPI 等其他框架确保了高度的安全性。Flask 还针对 CSRF 和 XSS 攻击等安全威胁提供出色的保护。

测试

Falcon 使用 unittest 和 Pytest 提供内置测试支持。Flask 和 Django 也支持单元测试。FastAPI 支持单元测试和星形测试功能。

Python Falcon - 环境设置

最新版本的 Falcon 需要 Python 3.5 或更高版本。安装 Falcon 最简单也是推荐的方法是使用 PIP 安装程序,最好是在虚拟环境中。

可以通过运行以下命令安装最新的稳定版本 -

pip3 install falcon

要验证安装是否已成功执行,请导入库并检查其版本。

>>> import falcon
>>>falcon.__version__
'3.1.0'

要安装最新的测试版,应使用以下命令 -

pip3 install --pre falcon

从早期版本开始,Falcon就支持WSGI。Falcon 应用程序可以在 Python 标准库模块wsgiref中内置 WSGI 服务器的帮助下运行。但是,它不适合生产环境,因为生产环境需要 WSGI 服务器,例如gunicorn、waitress 或uwsgi。

对于 Windows 上的 Falcon,可以使用Waitress,这是一种生产质量的纯 Python WSGI 服务器。像往常一样,使用 pip 安装程序安装它。

pip3 install waitress

Gunicorn服务器无法安装在 Windows 上。但是,它可以在Windows 10 上的Windows 子系统 Linux ( WSL ) 环境中使用。要在 Linux、WSL 或 Docker 容器内使用 Gunicorn,请使用

pip3 install gunicorn

如果要运行异步 Falcon 应用程序,则需要符合 ASGI 的应用程序服务器。Uvicorn 服务器可以在 Windows 和 Linux 系统上使用。

pip3 install uvicorn

Python Falcon - WSGI 与 ASGI

Web 服务器网关接口 (WSGI)

一些最流行的 Python Web 框架实现了 WSGI(代表Web 服务器网关接口)。WSGI本质上是一组Web服务器和Web应用程序之间通用接口的规范,由Web服务器软件实现,用于处理来自基于Python的Web应用程序的请求。WSGI 规范于 2003 年首次引入 (PEP 333),并于 2010 年更新 (PEP 3333)。

服务器通过传递以下参数来调用 WSGI 应用程序对象 -

  • environ - 一个 Python dict对象,类似于 CGI 环境变量和某些 WSGI 特定变量。

  • start_response - 应用程序使用的回调函数,用于返回其响应以及标头和状态代码。

该对象可以是 Python 中的任何可调用对象,例如函数、方法、类或其具有可用__call__()方法的实例。该应用程序对象必须返回一个由单字节字符串组成的迭代器。

def application (environ, start_response):
   ...
   ...
   return [("Hello World!".encode("utf-8")]

然而,启用 WSGI 的服务器在操作上是同步的,因此应用程序的效率不高。Python 在 3.4 版本中引入了asyncio模块作为标准库的一部分,开始支持异步编程。

asyncio模块提供了在 Python 应用程序中合并并发编程风格通常称为协作多任务)的能力。在这种方法中,操作系统不会阻碍不同进程之间的上下文切换。相反,一个进程会定期让出以容纳其他进程,以便许多应用程序可以同时运行。

在Python的3.5版本中,添加了asyncawait这两个关键字。使用 async 关键字定义的 Python 函数将成为协程,因此无法像普通函数一样运行。相反,我们需要使用asyncio.run (coroutine)来调用它。可以通过await关键字暂停一个协程的执行,直到另一个协程完成。

import asyncio
async def main():
   print('hello')
   await asyncio.sleep(5)
   print('world')

asyncio.run(main())

异步服务器网关接口(ASGI)

ASGI 代表异步服务器网关接口(根据其官方文档,它是 WSGI 的精神继承者),它为 Python Web 服务器、应用程序和框架添加了异步功能。

ASGI 应用程序是一个异步可调用对象(用户定义的函数或具有__call__()方法的类的对象)。它需要三个参数,如下 -

  • 范围-包含特定连接详细信息的字典

  • Send - 异步可调用,通过它可以将事件消息发送到客户端

  • 接收- 另一个异步可调用。应用程序可以接收来自客户端的事件消息。

以下是由异步函数表示的简单 ASGI 应用程序的原型 -

async def app(scope, receive, send):
   assert scope['type'] == 'http'
   await send({
   'type': 'http.response.start',
   'status': 200,
   'headers': [
      [b'content-type', b'text/plain'],
   ],
})
await send({
   'type': 'http.response.body',
   'body': b'Hello, world!',
})

Python Falcon - Hello World(WSGI)

要创建一个简单的 Hello World Falcon 应用程序,请首先导入库并声明 App 对象的实例。

import falcon
app = falcon.App()

Falcon遵循REST架构风格。声明一个资源类,其中包含一个或多个表示标准 HTTP 动词的方法。以下HelloResource类包含on_get()方法,预计在服务器收到GET请求时调用该方法。该方法返回 Hello World 响应。

class HelloResource:
   def on_get(self, req, resp):
   """Handles GET requests"""
   resp.status = falcon.HTTP_200
   resp.content_type = falcon.MEDIA_TEXT
   resp.text = (
      'Hello World'
   )

要调用此方法,我们需要将其注册到路由或 URL。Falcon 应用程序对象通过add_rule方法将处理程序方法分配给相应的 URL 来处理传入的请求。

hello = HelloResource()
app.add_route('/hello', hello)

Falcon 应用程序对象只不过是一个 WSGI 应用程序。我们可以使用Python标准库的wsgiref模块中内置的WSGI服务器。

from wsgiref.simple_server import make_server

if __name__ == '__main__':
   with make_server('', 8000, app) as httpd:
   print('Serving on port 8000...')
# Serve until process is killed
httpd.serve_forever()

例子

让我们把所有这些代码片段放在hellofalcon.py中

from wsgiref.simple_server import make_server

import falcon

app = falcon.App()

class HelloResource:
   def on_get(self, req, resp):
      """Handles GET requests"""
      resp.status = falcon.HTTP_200
      resp.content_type = falcon.MEDIA_TEXT
      resp.text = (
         'Hello World'
      )
hello = HelloResource()

app.add_route('/hello', hello)

if __name__ == '__main__':
   with make_server('', 8000, app) as httpd:
   print('Serving on port 8000...')
# Serve until process is killed
httpd.serve_forever()

从命令提示符运行此代码。

(falconenv) E:\falconenv>python hellofalcon.py
Serving on port 8000...

输出

在另一个终端中,运行 Curl 命令,如下所示 -

C:\Users\user>curl localhost:8000/hello
Hello World

您还可以打开浏览器窗口并输入上述 URL 来获取“Hello World”响应。

Python Falcon你好世界

Python Falcon - 女服务员

开发服务器不建议用于生产环境。开发服务器效率不高、不稳定或安全。

Waitress 是一个生产质量的纯 Python WSGI 服务器,具有非常可接受的性能。除了 Python 标准库中的依赖项之外,它没有任何依赖项。它在 Unix 和 Windows 上的 CPython 上运行。

确保工作环境中已安装Waitress服务器。该库包含服务类,其对象负责服务传入的请求。serve类的构造函数需要三个参数。

serve (app, host, port)

falcon应用程序对象是app参数。主机和端口默认值为 localhost 8080。监听参数是一个由主机:端口参数组合而成的字符串,默认为“0.0.0.0:8080”

例子

hellofalcon.py代码中,我们导入serve类而不是simple_server并实例化其对象,如下所示 -

from waitress import serve
import falcon
class HelloResource:
   def on_get(self, req, resp):
   """Handles GET requests"""
   resp.status = falcon.HTTP_200
   resp.content_type = falcon.MEDIA_TEXT
   resp.text = (
      'Hello World'
   )
app = falcon.App()
hello = HelloResource()
app.add_route('/hello', hello)
if __name__ == '__main__':
   serve(app, host='0.0.0.0', port=8000)

执行hellofalcon.py ,像之前一样在浏览器中访问http://localhost:8000/hellolink 。请注意,主机 0.0.0.0 使本地主机公开可见。

Waitress 服务器也可以从命令行启动,如下所示 -

waitress-serve --port=8000 hellofalcon:app

Python Falcon - ASGI

ASGI 代表异步服务器网关接口(根据其官方文档,它是 WSGI 的精神继承者),它为 Python Web 服务器、应用程序和框架添加了异步功能。

为了运行异步 Web 应用程序,我们需要一个 ASGI 应用程序服务器。流行的选择包括 -

  • 独角兽
  • 超级玉米

在本教程中,我们将使用Uvicorn服务器作为异步示例。

你好世界-ASGI

Falcon 的 ASGI 相关功能可在 falcon.asgi 模块中找到。因此,我们需要在一开始就导入它。

import falcon
import falcon.asgi

虽然资源类与前面的示例相同,但 on_get() 方法必须使用 async 关键字声明。我们必须获取 Falson 的 ASGI 应用程序的实例。

app = falcon.asgi.App()

例子

因此,ASGI 的 hellofalcon.py 将如下 -

import falcon
import falcon.asgi
class HelloResource:
   async def on_get(self, req, resp):
      """Handles GET requests"""
      resp.status = falcon.HTTP_200
      resp.content_type = falcon.MEDIA_TEXT
      resp.text = (
         'Hello World'
      )
app = falcon.asgi.App()
hello = HelloResource()
app.add_route('/hello', hello)

要运行该应用程序,请从命令行启动 Uvicorn 服务器,如下所示 -

uvicorn hellofalcon:app –reload

输出

打开浏览器并访问http://localhost:/8000/hello。您将在浏览器窗口中看到响应。

ASGI

蟒蛇Falcon - Uvicorn

Uvicorn 使用uvloophttptools库。它还提供对 HTTP/2 和 WebSockets 的支持,这是 WSGI 无法处理的。uvloop类似于内置的asyncio事件循环。httptools库处理 http 协议。

Falcon 的 ASGI 兼容应用程序使用以下命令在 Uvicorn 服务器上启动 -

Uvicorn hellofalcon:app – reload

--reload选项启用调试模式,这样 app.py 中的任何更改都会自动反映,并且客户端浏览器上的显示会自动刷新此外,可以使用以下命令行选项 -

--主机文本 将套接字绑定到该主机。[默认127.0.0.1]
--端口整数 将套接字绑定到此端口。[默认8000]
--uds 文本 绑定到 UNIX 域套接字。
--fd 整数 从此文件描述符绑定到套接字。
--重新加载 启用自动重新加载。
--reload-dir 路径 显式设置重新加载目录,默认当前工作目录。
--reload-包含文本 观看时包含文件。默认情况下包含“* .py
--reload-排除文本 监视文件时排除。
--重新加载延迟浮动 上一次检查和下一次检查之间的延迟默认 0.25
--loop [自动|异步|uvloop] 事件循环实现。[默认自动]
--http [自动|h11|httptools] HTTP协议的实现。[默认自动]
--接口自动|asgi|wsgi 选择应用程序界面。[默认自动]
--env-文件路径 环境配置文件。
--log-config 路径 记录配置文件。支持的格式.ini、.json、.yaml。
- 版本 显示 Uvicorn 版本并退出。
--app-dir 文本 在指定目录默认当前目录下查找APP
- 帮助 显示此消息并退出。

Uvicorn 服务器也可以从程序内部启动,而不是通过上面的命令行。为此,导入uvicorn模块并调用uvicorn.run()方法,如下所示 -

import uvicorn
if __name__ == "__main__":
   uvicorn.run("hellofalcon:app", host="0.0.0.0", port=8000, reload=True)

相应地更改 hellofalcon.py 代码,并从命令提示符执行相同的代码。结果可以通过curl命令或在浏览器中验证,如前所述。

Python Falcon - API 测试工具

Falcon 是一个适合开发 API 的简约框架。API 是两个应用程序之间的接口。API开发者在将其发布到生产环境中使用之前,需要对其功能、可靠性、稳定性、可扩展性、性能等进行测试。

有多种 API 测试工具可用于此目的。在本节中,我们将学习如何使用命令行工具CurlHTTPie以及名为Postman 的GUI 工具。

卷曲

cURL 是一个开源项目,提供 libcurl 库和一个名为curl 的命令行工具,可以使用各种协议传输数据。支持包括 HTTP 在内的 20 多种协议。首字母缩略词 cURL 代表客户端 URL。从命令行使用 Curl 的语法是 -

curl [options] [URL1, URL2,..]

URL 参数由依赖于协议的一个或多个 URL 字符串组成。可以使用各种选项自定义 Curl 命令。一些重要的命令行选项如下 -

  • – X:提及请求方法。默认情况下,Curl 假定 GET 为请求方法。要发送 POST、PUT 或 DELTETE 请求,必须使用此选项。例如 -

Curl –X DELETE http://localhost:8000/student/1
  • -H:该选项用于在请求中添加标头。例如 -

Curl –H "Content-Type: application/json" -X GET
http://localhost:8000/students
  • -i:当命令行中包含该选项时,将显示所有响应头。例如 -

Curl –I –X DELETE http://localhost:8000/student/2
  • -d:要在HTTP请求中包含数据进行处理,我们必须使用此选项,特别是当需要POST或PUT请求时。

Curl –H "Content-Type: application/json" -X PUT -d
"{"""marks""":"""50"""}" http://localhost:8000/students/3

HTTPie

HTTPie 是一个用 Python 编写的命令行工具。据说它是“人类的类似 cURL 的工具”。它支持表单和文件上传,并生成格式良好的彩色终端输出。与 Curl 相比,其富有表现力和直观的语法使其更易于使用。

例子

  • GET 请求- http GET localhost:8000/students

  • POST 请求- http POST localhost:8000/students id=4 name="aaa"%=50

  • PUT 请求- http PUT localhost:8000/students/2 id=3 name="Mathews"%=55

  • DEETE 请求- http DELETE localhost:8000/students/2

邮差

Postman是一个非常流行的API测试工具。它是一个 GUI 应用程序,与 Curl 和 HTTPie 不同。它以浏览器插件和桌面应用程序的形式提供。由于浏览器插件不接受基于 localhost 的 API 请求,因此我们需要从https://www.postman.com/downloads 下载桌面版本。

完成基于向导的安装后,启动 Postman 应用程序并创建新请求。

Python Falcon API1

下拉列表显示可供选择的各种 HTTP 请求类型。

Python Falcon API2

在请求 URL 字段中输入http://localhost:8000/hello 。右侧的响应窗格显示结果。

Python Falcon API3

稍后我们在 SQLite 数据库上测试 Falcon API 的 CRUD 操作时将使用相应的请求类型。

Python Falcon - 请求和响应

HTTP 协议规定,客户端向服务器发送 HTTP 请求,服务器应用特定的业务逻辑并制定响应,该响应被重定向到客户端。两者之间同步传输时,Python框架使用WSGI标准,而异步传输则遵循ASGI标准。Falcon支持两者。

WSGI/ASGI 服务器在上下文数据中提供请求和响应对象。这些对象被响应者、钩子、中间件等用作参数。对于 WSGI 应用程序,将处理falcon.Request类的实例。在 ASGI 应用程序中,它代表falcon.asgi.Request类。虽然不同,但这两个类都被设计为具有相似的属性和方法,以尽量减少混乱并更容易移植。

要求

Request 对象代表 HTTP 请求。由于它是由服务器提供的,因此该对象并不意味着由响应程序方法直接实例化。该对象提供了以下属性和方法,可在响应程序、挂钩和中间件方法中使用 -

  • method - 请求的 HTTP 方法(例如“GET”、“POST”等)

  • host - 主机请求标头字段

  • port - 用于请求的端口。返回给定模式的默认值(HTTP 为 80,HTTPS 为 443)

  • uri - 请求的完全限定 URI。

  • path - 请求 URI 的路径部分(不包括查询字符串)。

  • query_string - 请求 URI 的查询字符串部分,前面不带“?” 特点。

  • cookies - 名称/值 cookie 对的字典。

  • content_type - Content-Type 标头的值,如果标头丢失则为 None 。

  • Stream - 用于读取请求正文的类似文件的输入对象(如果有)。该对象提供对服务器数据流的直接访问并且是不可查找的。

  • bounded_stream - 流周围的类似文件的包装器

  • headers - 请求中的原始 HTTP 标头

  • params - 请求查询参数名称与其值的映射。

  • get_cookie_values(name) - 返回指定 cookie 的 Cookie 标头中提供的所有值。cookies 属性的别名。

  • get_media() - 返回请求流的反序列化形式。类似于媒体财产。

  • get_param(name) - 以字符串形式返回查询字符串参数的原始值。如果发布了带有application/x-wwwform-urlencoded媒体类型的 HTML 表单,Falcon 可以自动解析请求正文中的参数并将它们合并到查询字符串参数中。要启用此功能,请通过App.req_options将auto_parse_form_urlencoded设置为 True 。

回复

Response 对象表示服务器对客户端的 HTTP 响应。与 Request 对象一样,Response 对象也不应该由响应者直接实例化。

响应者、钩子函数或中间件方法通过访问以下属性和方法来操纵该对象 -

  • status - HTTP 状态代码,例如“200 OK”。这可以设置为http.HTTPStatus的成员、HTTP 状态行字符串或字节字符串或 int。Falcon 为常见状态代码提供了许多常量,以 HTTP_ 前缀开头,如 - falcon.HTTP_204

  • media - 通过falcon.RequestOptions配置的媒体处理程序支持的可序列化对象。

  • text - 表示响应内容的字符串。

  • body - 已弃用的文本别名。

  • data - 表示响应内容的字节字符串。

  • - 表示响应内容的类似文件的对象。

  • content_length - 设置内容长度标头。当未设置文本或数据属性时,它会手动设置内容长度。

  • content_type - 设置内容类型标头。Falcon 针对常见媒体类型的预定义常量包括 falcon.MEDIA_JSON、falcon.MEDIA_MSGPACK、falcon.MEDIA_YAML、falcon.MEDIA_XML、falcon.MEDIA_HTML、falcon.MEDIA_JS、falcon.MEDIA_TEXT、falcon.MEDIA_JPEG、falcon.MEDIA_PNG 和 falcon.MEDIA_GIF。

  • append_header (name, value) - 设置或附加此响应的标头。用于设置cookie。

  • delete_header (name) - 删除之前为此响应设置的标头。

  • get_header (name) - 检索给定标头的原始字符串值。

  • set_cookie (name, value) - 设置响应 cookie。可以多次调用此方法以将一个或多个 cookie 添加到响应中。

  • set_header (name, value) - 将此响应的标头设置为给定值。

  • set_stream (stream, content_length) - 设置流和content_length。

  • unset_cookie (name, domain=None, path=None) - 取消设置响应中的 cookie。此方法清除 cookie 的内容,并指示用户代理立即使自己的 cookie 副本失效。

Python Falcon - 资源类

Falcon 的设计借鉴了 REST 架构风格的几个关键概念。REST 代表关系状态传输。REST 定义了 Web 应用程序架构的Behave方式。

REST 是一种基于资源的架构。在这里,REST 服务器托管的所有内容(无论是文件、图像还是数据库表中的行)都被视为资源,可能有多种表示形式。REST API 提供对这些资源的受控访问,以便客户端可以检索和修改它们。

服务器的资源应该只有一个统一资源标识符 (URI)。它仅标识资源;它没有指定对该资源采取什么操作。相反,用户从一组标准方法中进行选择。用于对资源进行操作的 HTTP 动词或方法。POST、GET、PUT 和 DELETE 方法分别执行 CREATE、READ、UPDATE 和 DELETE 操作。

Falcon 使用普通的 Python 类来表示资源。这样的类充当应用程序中的控制器。它将传入请求转换为一个或多个内部操作,然后根据这些操作的结果编写返回给客户端的响应。

Python Falcon资源

每个资源类都定义了各种“响应程序”方法,一种对应于资源允许的每种 HTTP 方法。响应程序名称以“on_”开头,并根据它们处理的 HTTP 方法进行命名,如on_get()、on_post()、on_put()等。

在上面使用的hellofalcon.py示例代码中,HelloResource(资源类)有一个on_get()响应器方法。响应者必须始终定义至少两个参数来接收请求和响应对象。

import falcon
class HelloResource:
   def on_get(self, req, resp):
      """Handles GET requests"""
      resp.status = falcon.HTTP_200
      resp.content_type = falcon.MEDIA_TEXT
      resp.text = (
         'Hello World'
      )

对于ASGI应用程序,响应者必须是协程函数,即必须使用async关键字定义。

class HelloResource:
   async def on_get(self, req, resp):
      """Handles GET requests"""
      resp.status = falcon.HTTP_200
      resp.content_type = falcon.MEDIA_TEXT
      resp.text = (
         'Hello World'
      )

Request对象表示传入的 HTTP 请求。可以通过此对象访问请求标头、查询字符串参数以及与请求关联的其他元数据。

Response 对象表示应用程序对请求的 HTTP 响应。该对象的属性和方法设置状态、标头和正文数据。它还公开了一个类似字典的上下文属性,用于将任意数据传递给钩子和其他中间件方法。

请注意,上面示例中的HelloResource只是一个普通的 Python 类。它可以有任何名称;但是,约定将其命名为xxxResource

Python Falcon - 应用程序类

此类是基于 Falcon 的 WSGI 应用程序的主要入口点。此类的实例提供可调用的 WSGI 接口和路由引擎。

import falcon
app = falcon.App()

此类的__init __()构造函数采用以下关键字参数 -

  • media_type - 初始化 RequestOptions 和 ResponseOptions 时使用的媒体类型。Falcon 允许轻松且可定制的互联网媒体类型处理。默认情况下,Falcon 仅启用 JSON 和 HTML(URL 编码和多部分)表单的处理程序。

  • Falcon 支持的其他媒体类型由以下常量表示 -

    • falcon.MEDIA_JSON

    • falcon.MEDIA_MSGPACK

    • Falcon.MEDIA_MULTIPART

    • falcon.MEDIA_URLENCODED

    • falcon.MEDIA_YAML

    • falcon.MEDIA_XML

    • Falcon.MEDIA_HTML

    • Falcon.MEDIA_JS

    • Falcon.MEDIA_TEXT

    • Falcon.MEDIA_JPEG

    • Falcon.MEDIA_PNG

    • Falcon.MEDIA_GIF

  • request_type - 此参数的默认值是falcon.Request类。

  • response_type - 此参数的默认值为falcon.Response类。

为了使App对象可调用,它的类有一个__call__()方法。

__call__(self, env, start_response)

这是一个 WSGI 应用程序方法。WSGI 开发服务器或其他生产服务器(Waitress/Uvicorn)使用此对象来启动服务器实例并侦听来自客户端的请求。

App 类还定义了 add_route() 方法。

add_route(self, uri_template, resource)

此方法有助于将 URI 路径与资源类的对象关联起来。传入请求将根据一组 URI 模板路由到资源。如果路径与给定路由的模板匹配,则请求将被传递到关联的资源进行处理。根据请求方法,调用相应的响应程序方法。

例子

让我们将on_post()响应程序方法添加到HelloResource类并测试 GET 和 POST 请求的端点。

from waitress import serve
import falcon
import json
class HelloResource:
   def on_get(self, req, resp):
      resp.status = falcon.HTTP_200
      resp.content_type = falcon.MEDIA_TEXT
      resp.text = (
         'Hello World'
      )
   def on_post(self, req, resp):
      data=req.media
      nm=data['name']
      resp.status = falcon.HTTP_200
      resp.content_type = falcon.MEDIA_TEXT 
      resp.text = (
         'Hello '+nm
      )
app = falcon.App()
hello = HelloResource()
app.add_route('/hello', hello)
if __name__ == '__main__':
   serve(app, host='0.0.0.0', port=8000)

输出

使用 Waitress 服务器运行应用程序并使用 Curl 检查响应。要响应 GET 请求,请使用以下命令 -

C:\Users\User>curl localhost:8000/hello
Hello World

我们通过 POST 方法将一些数据发送到 /hello URL,如下所示 -

C:\Users\User>curl -i -H "Content-Type:application/json" -X
POST -d "{"""name""":"""John"""}" http://localhost:8000/hello
HTTP/1.1 200 OK
Content-Length: 10
Content-Type: text/plain; charset=utf-8
Date: Sun, 17 Apr 2022 07:06:20 GMT
Server: waitress
Hello John

要添加到静态文件目录的路由,Falcon 有add_static_route()方法。

add_static_route(self, prefix, directory, downloadable=False,
fallback_filename=None)

prefix 参数是与该路由匹配的路径前缀。目录参数是提供文件的源目录。如果您想在响应中包含 ContentDisposition 标头,则可下载参数设置为 True。Fallback_filename默认为 None,但可以在找不到请求的文件时指定

add_error_handler ()方法用于注册一种或多种异常类型的处理程序。

add_error_handler(self, exception, handler=None)

ASGI 可调用 App 类拥有相同的方法。它在 falcon.asgi 模块中定义。

import falcon.asgi
app=falcon.asgi.App()

请注意,ASGI 应用程序中资源类的响应者必须是协程(使用async关键字定义)而不是普通方法。

class HelloResource:
   async def on_get(self, req, resp):
      """Handles GET requests"""
      resp.status = falcon.HTTP_200
      resp.content_type = falcon.MEDIA_TEXT
      resp.text = (
         'Hello World'
      )

Python Falcon - 路由

Falcon采用RESTful架构风格。因此它使用基于资源的路由。资源类负责处理响应者的 HTTP 方法,这些方法本质上是名称以 on_ 开头并以小写 HTTP 方法名称结尾的类方法(例如 on_get()、on_patch()、on_delete()) .)。Falcon 应用程序对象的add_route ()方法将其路由器与资源类的实例关联起来。

在上面使用的 Hellofalcon.py 示例中,当客户端分别通过 GET 和 POST 方法请求/hello路由时,将调用on_get()on_post()响应程序。

如果没有路由与请求匹配,则会引发HTTPRouteNotFound实例。另一方面,如果路由匹配但资源未实现所请求的 HTTP 方法的响应程序,则默认响应程序将引发HTTPMethodNotAllowed的实例。

现场转换器

Falcon 的路由机制允许 URL 将参数传递给响应者。URL 由三部分组成: 协议(例如http://https://),后跟 IP 地址或主机名。URL 的第一个/主机名之后的剩余部分称为路径或端点。要传递的参数位于端点之后。

路由

它充当资源标识符,例如唯一 ID 或主键。参数名称括在大括号中。除了请求和响应之外,路径参数的值还将转到响应者方法中定义的参数。

在以下示例中,路由器将资源类对象与由端点后面的参数组成的 URL 相关联。

from waitress import serve
import falcon
import json
class HelloResource:
   def on_get(self, req, resp, nm):
      """Handles GET requests"""
      resp.status = falcon.HTTP_200
      resp.content_type = falcon.MEDIA_TEXT
      resp.text = (
         'Hello '+nm
      )
app = falcon.App()
hello = HelloResource()
app.add_route('/hello/{nm}', hello)
if __name__ == '__main__':
   serve(app, host='0.0.0.0', port=8000)

我们可以看到on_get()响应者方法有一个额外的参数 nm 来接受从 URL 路由解析的数据。让我们使用HTTPie工具测试http://localhost:8000/hello/Priya

>http GET localhost:8000/hello/Priya
HTTP/1.1 200 OK
Content-Length: 11
Content-Type: text/plain; charset=utf-8
Date: Mon, 18 Apr 2022 12:27:35 GMT
Server: waitress
Hello Priya

路径参数解析为的默认数据类型是str(即字符串)。然而,Falcon 的路由器引擎具有以下内置字段转换器,使用它们也可以将它们读入其他数据类型。

  • IntConverter - 此类在falcon.routing模块中定义。构造函数使用以下参数 -

IntConverter(num_digits=None, min=None, max=None)

    在哪里,

    • num_digits - 该值必须具有给定的位数。

    • min - 参数的最小要求值

    • max - 参数的最大允许值。

    例如,以下add_route()函数接受 1 到 100 之间的整数作为rollno

app.add_route('/student/{rollno:int(1,1,100}', StudentResource())
  • UUIDConverter - falcon.routing 模块中的此类将 32 个十六进制数字的字符串转换为 UUID(通用唯一标识符)。

  • DateTimeConverter - 将参数字符串转换为日期时间变量。参数必须是strptime()函数识别的任何格式的字符串,默认为'%Y-%m-%dT%H:%M:%SZ'

格式字符串使用以下格式代码 -

%A 工作日缩写名称 周日、周一
%A 工作日的完整名称 星期天星期一
%d 以零填充小数表示的月份中的某一天 01, 02
%-d 十进制数形式的月份中的某一天 1, 2..
%b 月份名称缩写 一月、二月
%m 月份作为补零十进制数 01, 02
%B 完整的月份名称 一月二月
%-y 没有世纪的年份作为十进制数 0, 99
%Y 年份,世纪为十进制数 2000年、1999年
%H 小时(24 小时制)作为补零十进制数 01, 23
%p 区域设置的 AM 或 PM 上午下午
%-M 以十进制表示的分钟 1, 59
%-S 第二位十进制数 1, 59

在以下示例中,add_route()函数将带有两个参数的 URL 与 Resource 对象相关联。第一个参数 nm 默认是一个字符串。第二个参数 Age 使用IntConverter

from waitress import serve
import falcon
import json
class HelloResource:
   def on_get(self, req, resp, nm,age):
      """Handles GET requests"""
      retvalue={"name":nm, "age":age}
      resp.body=json.dumps(retvalue)
      resp.status = falcon.HTTP_200 
      resp.content_type = falcon.MEDIA_JSON
app = falcon.App()
hello = HelloResource()
app.add_route('/hello/{nm}/{age:int}', hello)
if __name__ == '__main__':
   serve(app, host='0.0.0.0', port=8000)

请注意,on_get()响应程序使用路径参数来形成一个dict对象 – retvalue。然后将其 JSON 表示形式指定为响应正文的值并返回给客户端。如前所述,JSON 是 Falcon 响应对象的默认内容类型。

启动 Waitress 服务器并在HTTPie的帮助下检查 URL http://localhost:8000/hello/Priya/21的响应。

http GET localhost:8000/hello/Priya/21
HTTP/1.1 200 OK
Content-Length: 28
Content-Type: application/json
Date: Fri, 22 Apr 2022 14:22:47 GMT
Server: waitress {
   "age": 21,
   "name": "Priya"
}

您还可以在浏览器中检查响应,如下所示 -

路由你好

Python Falcon - 带后缀的响应者

为了理解后缀响应者的概念和需求,让我们定义一个StudentResource类。它由一个on_get()响应程序组成,该响应程序将学生的dict对象列表转换为 JSON 并作为其响应返回。

我们还添加on_post()响应程序,该响应程序从传入请求中读取数据并在列表中添加一个新的dict对象。

import falcon
import json
from waitress import serve
students = [
   {"id": 1, "name": "Ravi", "percent": 75.50},
   {"id": 2, "name": "Mona", "percent": 80.00},
   {"id": 3, "name": "Mathews", "percent": 65.25},
]
class StudentResource:
   def on_get(self, req, resp):
      resp.text = json.dumps(students)
      resp.status = falcon.HTTP_OK
      resp.content_type = falcon.MEDIA_JSON
   def on_post(self, req, resp):
      student = json.load(req.bounded_stream)
      students.append(student)
      resp.text = "Student added successfully."
      resp.status = falcon.HTTP_OK
      resp.content_type = falcon.MEDIA_TEXT

使用Falcon 的 App 对象的add_route()函数,我们添加/students路由。

app = falcon.App()
app.add_route("/students", StudentResource())

启动服务器后,我们可以从 HTTPie 命令行测试 GET 和 POST 请求 -

http GET localhost:8000/students
HTTP/1.1 200 OK
Content-Length: 187
Content-Type: application/json
Date: Mon, 18 Apr 2022 06:21:02 GMT
Server: waitress
[
   {
      "id": 1,
      "name": "Ravi",
      "percent": 75.5
   },
   {
      "id": 2,
      "name": "Mona",
      "percent": 80.0
   },
   {
      "id": 3,
      "name": "Mathews",
      "percent": 65.25
   }
]
http POST localhost:8000/students id=4 name="Prachi"
percent=59.90
HTTP/1.1 200 OK
Content-Length: 27
Content-Type: text/plain; charset=utf-8
Date: Mon, 18 Apr 2022 06:20:51 GMT
Server: waitress
Student added successfully.

再次调用on_get()确认添加新学生资源。

http GET localhost:8000/students
HTTP/1.1 200 OK
Content-Length: 187
Content-Type: application/json
Date: Mon, 18 Apr 2022 06:21:02 GMT
Server: waitress
[
   {
      "id": 1,
      "name": "Ravi",
      "percent": 75.5
   },
   {
      "id": 2,
      "name": "Mona",
      "percent": 80.0
   },
   {
      "id": 3,
      "name": "Mathews",
      "percent": 65.25
   },
   {
      "id": "4",
      "name": "Prachi",
      "percent": "59.90"
   }
]

在此阶段,我们希望在StudentResource类中有一个 GET 响应器方法,该方法从 URL 读取 id 参数并从列表中检索相应的dict对象。

换句话说,/student/{id} 格式的 URL 应该与资源类中的 GET 方法相关联。但显然,一个类不能有两个同名的方法。因此,我们定义在add_route()方法中使用后缀参数来区分on_get()响应者的两个定义。

通过指定suffix ='student' ,将带有 id 参数的路由添加到 Application 对象。

app.add_route("/students/{id:int}", StudentResource(), suffix='student')

现在,我们可以使用此后缀添加on_get()方法的另一个定义,以便该响应者的名称为on_get_student(),如下所示 -

def on_get_student(self, req, resp, id):
   resp.text = json.dumps(students[id-1])
   resp.status = falcon.HTTP_OK
   resp.content_type = falcon.MEDIA_JSON

添加新路由和on_get_student()响应程序后启动Waitress服务器并测试此 URL,如下所示 -

http GET localhost:8000/students/2
HTTP/1.1 200 OK
Content-Length: 42
Content-Type: application/json
Date: Mon, 18 Apr 2022 06:21:05 GMTy
Server: waitress
{
   "id": 2,
   "name": "Mona",
   "percent": 80.0
}

请注意,当客户端使用适当的请求标头请求URL 路由/students/{id:int}时,也会调用on_put()响应程序(用于更新资源)和on_delete()响应程序(用于删除资源)。

我们已经添加了这条以学生为后缀的路线。因此,on_put_student()方法解析整型变量中的路径参数。将获取具有给定 id 的项目的 JSON 表示形式,并使用 PUT 请求中提供的数据进行更新。

def on_put_student(self, req, resp, id):
   student=students[id-1]
   data = json.load(req.bounded_stream)

   student.update(data)
   resp.text = json.dumps(student)
   resp.status = falcon.HTTP_OK
   resp.content_type = falcon.MEDIA_JSON

on_delete_student ()响应程序只是删除具有 DELETE 请求中指定的 id 的项目。返回剩余资源列表。

def on_delete_student(self, req, resp, id):
   students.pop(id-1)
   resp.text = json.dumps(students)
   resp.status = falcon.HTTP_OK
   resp.content_type = falcon.MEDIA_JSON

我们可以使用HTTPie命令测试 API 的 PUT 和 DELETE 操作-

http PUT localhost:8000/students/2 id=3 name="Mathews"
percent=55
HTTP/1.1 200 OK
Content-Length: 46
Content-Type: application/json
Date: Sat, 18 Apr 2022 10:13:00 GMT
Server: waitress
{
   "id": "3",
   "name": "Mathews",
   "percent": "55"
}
http DELETE localhost:8000/students/2
HTTP/1.1 200 OK
Content-Length: 92
Content-Type: application/json
Date: Sat, 18 Apr 2022 10:18:00 GMT
Server: waitress
[
   {
      "id": 1,
      "name": "Ravi",
      "percent": 75.5
   },
   {
      "id": 3,
      "name": "Mathews",
      "percent": 65.25
   }
]

该 API ( studentapi.py )的完整代码如下 -

import falcon
import json
from waitress import serve
students = [
   {"id": 1, "name": "Ravi", "percent": 75.50},
   {"id": 2, "name": "Mona", "percent": 80.00},
   {"id": 3, "name": "Mathews", "percent": 65.25},
]
class StudentResource:
   def on_get(self, req, resp):
      resp.text = json.dumps(students)
      resp.status = falcon.HTTP_OK
      resp.content_type = falcon.MEDIA_JSON
   def on_post(self, req, resp):
      student = json.load(req.bounded_stream)
      students.append(student)
      resp.text = "Student added successfully."
      resp.status = falcon.HTTP_OK
      resp.content_type = falcon.MEDIA_TEXT
   def on_get_student(self, req, resp, id):
      resp.text = json.dumps(students[id-1])
      resp.status = falcon.HTTP_OK
      resp.content_type = falcon.MEDIA_JSON
   def on_put_student(self, req, resp, id):
      student=students[id-1]
      data = json.load(req.bounded_stream)

      student.update(data)

      resp.text = json.dumps(student)
      resp.status = falcon.HTTP_OK
      resp.content_type = falcon.MEDIA_JSON
   def on_delete_student(self, req, resp, id):
      students.pop(id-1)
      print (students)
      resp.text = json.dumps(students)
      resp.status = falcon.HTTP_OK
      resp.content_type = falcon.MEDIA_JSON
app = falcon.App()
app.add_route("/students", StudentResource())
app.add_route("/students/{id:int}", StudentResource(), suffix='student')
if __name__ == '__main__':
   serve(app, host='0.0.0.0', port=8000)

Python Falcon - 检查模块

检查模块是一个方便的工具,它提供有关注册路由和 Falcon 应用程序其他组件(例如中间件、接收器等)的信息。

应用程序的检查可以通过两种方式完成:CLI 工具和编程方式。falcon -inspect -tool CLI 脚本从命令行执行,给出声明 Falcon 应用程序对象的 Python 脚本的名称。

例如,检查Studentapi.py中的应用程序对象-

falcon-inspect-app studentapi:app
Falcon App (WSGI)
Routes:
   ⇒ /students - StudentResource:
   ├── GET - on_get
   └── POST - on_post
   ⇒ /students/{id:int} - StudentResource:
   ├── DELETE - on_delete_student
   ├── GET - on_get_student
   └── PUT - on_put_student

输出显示资源类中注册的路由和响应程序方法。要以编程方式执行检查,请使用应用程序对象作为检查模块中inspect_app()函数的参数。

from falcon import inspect
from studentapi import app
app_info = inspect.inspect_app(app)
print(app_info)

将上面的脚本保存为inspectapi.py并从命令行运行它。

python inspectapi.py
Falcon App (WSGI)
Routes:
   ⇒ /students - StudentResource:
   ├── GET - on_get
   └── POST - on_post
   ⇒ /students/{id:int} - StudentResource:
   ├── DELETE - on_delete_student
   ├── GET - on_get_student
   └── PUT - on_put_student

Python Falcon - Jinja2 模板

Falcon 库主要用于构建 API 和微服务。因此,默认情况下,Falcon 响应程序返回 JSON 响应。但是,如果内容类型更改为falcon.MEDIA_HTML,则可以呈现 HTML 输出。

渲染带有可变数据的 HTML 内容是非常乏味的。为此,使用了 Web 模板库。许多 Python Web 框架都捆绑了特定的模板库。但 Falcon 作为一个极简主义的微型框架并没有预先与任何人捆绑在一起。

Jinja2是许多 Python 框架使用的最流行的模板库之一。在本节中,我们将了解如何将 inja2 与 Falcon 应用程序一起使用。jinja2 是一种快速且设计人员友好的模板语言,易于配置和调试。其沙盒环境可以轻松防止不受信任的代码执行、禁止潜在的不安全数据以及防止跨站脚本攻击(称为XSS 攻击)。

jinja2的另一个非常强大的功能是模板继承,其中您可以定义具有公共设计功能的基本模板,子模板可以覆盖这些功能。

首先,使用 PIP 实用程序在当前 Python 环境中安装jinja2 。

pip3 install jinja2

你好世界模板

jinja2模块定义了一个 Template 类。Template 对象是通过读取包含 HTML 脚本(扩展名为 .html)的文件的内容来获取的。通过调用此 Template 对象的render()方法,可以将 HTML 响应呈现到客户端浏览器。Response 对象的 content_type属性必须设置为falcon.MEDIA_HTML

让我们将以下 HTML 脚本保存为应用程序文件夹中的hello.py 。

<html>
   <body>
      <h2>Hello World</h2>
   </body>
</html>

例子

下面资源类中的 on_get() 响应程序读取此文件并将其呈现为 HTML响应

import uvicorn
import falcon
import falcon.asgi
from jinja2 import Template
class HelloResource:
   async def on_get(self, req, resp):
      resp.status = falcon.HTTP_200
      resp.content_type = 'text/html'
      fp=open("hello.html","r")
      tempobj=Template(fp.read())
      resp.body=tempobj.render()
app = falcon.asgi.App()
hello = HelloResource()
app.add_route('/hello', hello)
if __name__ == "__main__":
   uvicorn.run("hello:app", host="0.0.0.0", port=8000, reload=True)

输出

运行上面的Python代码并在浏览器中访问http://localhost:8000/hello链接。

金贾2

模板变量

jinja2是一个服务器端模板库。通过将 jinja2 模板语言的各种元素作为占位符放置在 HTML 脚本内的适当分隔符内,将网页构建为模板。模板引擎读取 HTML 脚本,用服务器上的上下文数据替换占位符,重新组装 HTML,并将其呈现给客户端。

Template.render ()函数有一个可选的上下文字典参数。该字典的关键属性成为模板变量。这有助于在网页中呈现响应者传递的数据。

例子

在以下示例中,路由/hello/nm注册到资源对象,其中 nm 是路径参数。on_get ()响应程序将其作为上下文传递给从网页获取的模板对象。

import uvicorn
import falcon
import falcon.asgi
from jinja2 import Template
class HelloResource:
   async def on_get(self, req, resp, nm):
      resp.status = falcon.HTTP_200
      resp.content_type = 'text/html'
      fp=open("hello.html","r")
      tempobj=Template(fp.read())
      resp.body=tempobj.render({'name':nm})
app = falcon.asgi.App()
hello = HelloResource()
app.add_route('/hello/{nm}', hello)
if __name__ == "__main__":
   uvicorn.run("hello:app", host="0.0.0.0", port=8000, reload=True)

hello.html读取模板变量名中的路径参数它充当 HTML 脚本中的占位符。它被放入{{}}符号中,以便其值显示为 HTML 响应。

<html>
   <body>
      <h2>Hello {{ name }}</h2>
   </body>
</html>

输出

运行 Python 代码并输入http://localhost:8000/hello/Priya作为 URL。浏览器显示以下输出 -

金贾2 你好

jinja2 模板中的循环

如果响应者传递任何 Python 可迭代对象(例如列表、元组或字典),则可以使用其循环构造语法在 jinja2 模板内遍历其元素。

{% for item in collection %}
HTML block
{% endfor %}

在以下示例中,on_get()响应程序将作为dict对象列表的 Students 对象发送到模板list.html。它依次遍历数据并将其呈现为 HTML 表。

import falcon
import json
from waitress import serve
from jinja2 import Template
students = [
   {"id": 1, "name": "Ravi", "percent": 75.50},
   {"id": 2, "name": "Mona", "percent": 80.00},
   {"id": 3, "name": "Mathews", "percent": 65.25},
]
class StudentResource:
   def on_get(self, req, resp):
      resp.status = falcon.HTTP_OK
      resp.content_type = falcon.MEDIA_HTML
      fp=open("list.html","r")
      tempobj=Template(fp.read())
      resp.body=tempobj.render({'students':students})

list.html是 jinja2 模板。它接收作为字典对象列表的学生对象,并将每个键的值放入表的 <td>..<.td> 元素中。

<html>
<body>
<table border=1>
   <thead> <tr>
      <th>Student ID</th> <th>Student Name</th>
      <th>percentage</th>
      <th>Actions</th>
   </tr> </thead>
   <tbody>
   {% for Student in students %}
   <tr> <td>{{ Student.id }}</td> <td>{{ Student.name }}</td>
      <td>{{ Student.percent }}</td>
      <td>
         <a href="#">Edit</a>
         <a href="#">Delete</a>
      </td> </tr>
   {% endfor %}
   </tbody>
</table>
</body>
</html>

在浏览器地址栏中访问/students路由。学生列表显示在浏览器中。

Jinja2 图像

HTML 表单模板

在本节中,我们将了解 Falcon 如何从 HTML 表单读取数据。让我们将以下 HTML 脚本保存为 myform.html。我们将使用它来获取 Template 对象并渲染它。

<html>
<body>
   <form method="POST" action="http://localhost:8000/students">
   <p>Student Id: <input type="text" name="id"/> </p>
   <p>student Name: <input type="text" name="name"/> </p>
   <p>Percentage: <input type="text" name="percent"/> </p>
   <p><input type="submit"> </p>
</body>
</html>

Falcon App 对象在 Hello.py 文件中声明,该文件还有一个映射到/adddnew路由的资源类。on_get ()响应程序读取myform.html并呈现相同的内容。将显示 HTML 表单。表单通过POST方法提交到/students路径。

为了能够读取表单数据,falcon.RequestOptions类的auto_parse_form_urlencoded属性必须设置为 True。

app = falcon.App()
app.req_options.auto_parse_form_urlencoded = True

在这里,我们还从Student.py导入StudentResource类。on_get ()响应程序呈现学生列表。

当用户填写并提交表单时,将调用 on_post() 响应程序此方法收集req.params属性中的表单数据,该属性只不过是表单元素及其值的字典。然后添加学生词典。

def on_post(self, req,