Falcon is a light-weight Python web API framework for building high-performance microservices, app backends, and higher-level frameworks. This tutorial will give an introduction to Falcon, its major features, and how to install Falcon. We’ll also build a simple RESTful API with Falcon.
Falcon是一个轻量级的Python Web API框架,用于构建高性能微服务,应用程序后端和更高级别的框架。 本教程将介绍Falcon,其主要功能以及如何安装Falcon。 我们还将使用Falcon构建一个简单的RESTful API。
(Advantages of Falcon Compared to Other Frameworks)
According to the documentation, some of the features of Falcon that make it superior to other Python frameworks include:
根据文档 ,Falcon使其优于其他Python框架的一些功能包括:
Speed — Falcon requests work several times faster than most other Python frameworks. Additionally, Falcon works well when combined with Cython, giving it an extra speed boost; it also works well with PyPy.
速度 — Falcon请求的工作速度比大多数其他Python框架快几倍。 此外,Falcon与Cython结合使用时效果很好,从而可以额外提高速度。 它也可以与PyPy一起使用 。
Flexibility — Falcon leaves a lot of the implementation details to you, the developer; hence there’s freedom to customize your implementation.
灵活性 -猎鹰向开发人员留下了许多实施细节; 因此,可以自由定制您的实现。
Ease of debugging — In Falcon, logic paths are understandable and straightforward, which makes it easier to reason with the code and perform code debugging.
易于调试 -在Falcon中,逻辑路径是易于理解和直接的,这使得对代码进行推理和执行代码调试更加容易。
(Features)
- Highly-optimized code base
- Intuitive routing via URI templates and REST-inspired resource classes
- Easy access to headers and bodies through request and response classes
- DRY request processing via middleware components and hooks
- Idiomatic HTTP error responses
- Straightforward exception handling
(Building a RESTful API With Falcon)
In this example, you will build a bucket list API. A bucket list is simply a list of things you’d like to do before you die. Let’s get started. Create the project directory and set up a virtual environment.
在此示例中,您将构建存储桶列表API。 遗愿清单只是死亡前您想做的事情的清单。 让我们开始吧。 创建项目目录并设置虚拟环境。
mkdir bucketlist
cd bucketlist
virtualenv .venv
source .venv/bin/activate
(Install Falcon)
Installing Falcon is as simple as:
安装Falcon非常简单:
pip install falcon
Create another folder app
inside the bucketlist
directory and mark it as a Python module by creating an empty __init__.py
file in it:
在bucketlist
目录中创建另一个文件夹app
,并通过在其中创建一个空的__init__.py
文件将其标记为Python模块:
mkdir app
touch app/__init__.py
Inside the app folder, create a file main.py
and add the following code.
在app文件夹中,创建文件main.py
并添加以下代码。
import falcon
application = falcon.API()
if __name__ == "__main__":
application.run(host="0.0.0.0", debug=True)
Falcon framework uses the concept of a resource. Resources are things that can be accessed by a URL. We will discuss this further when we start building the views for our application.
Falcon框架使用资源的概念。 资源是可以通过URL访问的东西。 当我们开始为应用程序构建视图时,我们将进一步讨论。
Edit main.py
file to look something like this:
编辑main.py
文件,如下所示:
import falcon
import json
class IndexResource(object):
def on_get(self, req, res):
res.status = falcon.HTTP_200
res.body = json.dumps({"success": "My first falcon app"})
application = falcon.API()
application.add_route('/', IndexResource())
if __name__ == "__main__":
application.run(host="0.0.0.0", debug=True)
Falcon speaks WSGI, and so to serve a Falcon app, you will need a WSGI server. In this tutorial, we will be using Gunicorn to serve our app, so let’s install Gunicorn.
Falcon说WSGI,因此要提供Falcon应用程序,您将需要WSGI服务器。 在本教程中,我们将使用Gunicorn服务我们的应用程序,因此让我们安装Gunicorn。
pip install gunicorn
Now run your application by issuing gunicorn -b 0.0.0.0:8000 — reload main:application
. Navigate to http://127.0.0.1
, and you should see your representation of the IndexResource
.
现在,通过发出gunicorn -b 0.0.0.0:8000 — reload main:application
运行您的应用gunicorn -b 0.0.0.0:8000 — reload main:application
。 导航到http://127.0.0.1
,您应该看到IndexResource
的表示IndexResource
。
Let’s now get started on creating the blocks for our application:
现在让我们开始为我们的应用程序创建块:
(Configure database)
We will use the PostgreSQL database because it’s robust and stable.
我们将使用PostgreSQL数据库,因为它既健壮又稳定。
PostgreSQL should be installed on your machine. Install the psycopg2 driver library in our application
PostgreSQL应该安装在您的机器上。 在我们的应用程序中安装psycopg2驱动程序库
pip install psycopg2
(Create database and user)
Create a database “bucketlist” and assign a user.
创建一个数据库“ bucketlist”并分配一个用户。
Switch over to the Postgres account on your machine by typing:
通过输入以下命令切换到计算机上的Postgres帐户:
sudo su postgres
Access a Postgres prompt:
访问Postgres提示:
psql
Create bucketlist database:
创建存储桶列表数据库:
CREATE DATABASE bucketlist;
Create role:
创建角色:
CREATE ROLE pat WITH LOGIN PASSWORD ‘my_password’;
Grant access to the user:
向用户授予访问权限:
GRANT ALL PRIVILEGES ON DATABASE bucketlist TO pat;
We will use the SQLAlchemy extension to manage our application. This extension provides an ORM wrapper for the SQLAlchemy project.
我们将使用SQLAlchemy扩展来管理我们的应用程序。 该扩展为SQLAlchemy项目提供了一个ORM包装器。
Create a file config.py
in the app directory and add the configurations you set up for the database above. The config file should look something like this.
在app目录中创建一个文件config.py
,并在上面添加您为数据库设置的配置。 配置文件应如下所示。
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, scoped_session
import logging
DATABASE_URL = 'postgresql+psycopg2://pat:my_password@localhost/bucketlist'
DB_ECHO = False
DB_AUTOCOMMIT = True
def get_engine(uri):
logging.info('Connecting to database..')
options = {
'echo': DB_ECHO,
'execution_options': {
'autocommit': DB_AUTOCOMMIT
}
}
return create_engine(uri, **options)
db_session = scoped_session(sessionmaker())
engine = get_engine(DATABASE_URL)
(Create models)
Create a file models.py
in the app directory and write the following code.
在应用目录中创建一个文件models.py
并编写以下代码。
from sqlalchemy import Column, Integer, String
from sqlalchemy import DateTime, func
from sqlalchemy.ext.declarative import declarative_base, declared_attr
import datetime
class BaseModel(object):
@declared_attr
def __tablename__(self):
return self.__name__.lower()
def to_dict(self):
intersection = set(self.__table__.columns.keys()) & set(self.FIELDS)
return dict(map(
lambda key:
(key,
(lambda value: self.FIELDS[key](value) if value else None)
(getattr(self, key))),
intersection))
FIELDS = {}
Base = declarative_base(cls=BaseModel)
class Task(Base):
id = Column(Integer, primary_key=True)
task_name = Column(String(50), nullable=False)
created = Column(DateTime, default=datetime.datetime.utcnow)
modified = Column(DateTime, default=datetime.datetime.utcnow)
def __repr__(self):
return "<Task(task_name='%s',is_finished='%s')>" % \
(self.task_name, self.is_finished)
FIELDS = {
'task_name': str,
}
FIELDS.update(Base.FIELDS)
Here we declare a base class that all schema objects will inherit from. The to_dict
function will manage our queries by returning results in dictionary form.
在这里,我们声明所有模式对象都将继承的基类。 to_dict
函数将通过以字典形式返回结果来管理我们的查询。
The declarative_base()
returns a new base class from which all mapped classes should inherit.
declarative_base()
返回一个新的基类,所有映射的类都应从该基类继承。
In config.py
, update the file to use our models. Here we use the base class from our models to declare metadata that will be used to issue CREATE
statements for all tables in our models.
在config.py
,更新文件以使用我们的模型。 在这里,我们使用模型中的基类声明元数据,该元数据将用于为模型中的所有表发出CREATE
语句。
#app/config.py
.....
def init_session():
db_session.configure(bind=engine)
from app.models import Base
Base.metadata.create_all(engine)
(Requests and responses)
Each responder in a resource receives a Request object that can be used to read the headers, query parameters, and body of the request.
资源中的每个响应者都会收到一个Request对象,该对象可用于读取标头,查询参数和请求的正文。
HTTP request and HTTP response are represented by req
and resp
, respectively.
HTTP请求和HTTP响应分别由req
和resp
表示。
(Create middleware for managing requests and responses)
A middleware is a Python class that hooks into the request/response lifecycle.
中间件是连接到请求/响应生命周期的Python类。
Create a new file middleware.py
in the app directory and add the following middleware class:
创建一个新文件middleware.py
在app目录中,并添加以下中间件类:
# app/middleware.py
import json
class JSONTranslator(object):
def process_request(self, req, resp):
if req.content_length in (None, 0):
# Nothing to do
return
body = req.stream.read()
if not body:
raise falcon.HTTPBadRequest('Empty request body',
'A valid JSON is required.')
try:
req.context['doc'] = json.loads(body.decode('utf-8'))
except (ValueError, UnicodeDecodeError):
raise falcon.HTTPError(falcon.HTTP_753,
'Malformed JSON',
'Could not decode the request body.')
def process_response(self, req, resp, resource):
if 'result' not in resp.context:
return
resp.body = json.dumps(resp.context['result'])
The class above demonstrates how to read headers and query parameters, handle errors, and how to work with request and response bodies.
上面的类演示了如何读取标头和查询参数,如何处理错误以及如何使用请求和响应主体。
(Configure WSGI server to load the middleware and initialize the database)
Edit the main.py
file to include the middleware, and initialize the database as shown below.
编辑main.py
文件以包含中间件,并如下所示初始化数据库。
import falcon
from app.config import db_session, init_session
from app.views import JSONTranslator
application = falcon.API(middleware=[
JSONTranslator()
])
init_session()
if __name__ == "__main__":
application.run(host="127.0.0.1", debug=True)
(Create resources)
As we mentioned at the beginning of this tutorial, Falcon uses the concept of a resource, and Python classes usually represent these resources. They convert the incoming response into an action and deliver a response back to the client.
正如我们在本教程开始时提到的那样,Falcon使用资源的概念,Python类通常表示这些资源。 他们将传入的响应转换为操作,然后将响应传递回客户端。
For a resource to provide support for any HTTP method, you add an on_*()
method to the class, where *
is any one of the common HTTP methods, in lowercase, i.e., on_get()
, on_put()
, etc.
为了使资源支持任何HTTP方法,请将on_*()
方法添加到类中,其中*
是常用的HTTP方法中的任何一种,均使用小写形式,即on_get()
, on_put()
等。
Although resources are just regular classes, we are going to create a custom base class, BaseResource
, from which we will inherit when we start creating our resources.
尽管资源只是常规类,但我们将创建一个自定义基类BaseResource
,当我们开始创建资源时,将从该基类继承。
Let’s start by creating a tasks resource. In app/views.py
add the following code to:
让我们从创建任务开始 资源。 在app/views.py
将以下代码添加到:
import falcon
from app.models import Task
from app.config import db_session
try:
from collections import OrderedDict
except ImportError:
OrderedDict = dict
class BaseResource(object):
def success(self, resp, data=None):
resp.status = falcon.HTTP_200
obj = OrderedDict()
obj['status'] = 200
obj['data'] = data
obj['message'] = 'OK'
resp.body = json.dumps(obj)
class TaskResource(BaseResource):
def on_get(self, req, resp):
tasks = db_session.query(Task).all()
obj = [task.to_dict() for task in tasks]
self.success(resp, obj)
def on_post(self, req, resp):
task_details = req.context['doc']
task = Task(task_name=task_details['task_name'])
db_session.add(task)
self.success(resp, "Task created successfullly")
The TaskResource
defines two methods, on_get()
for obtaining all the recorded tasks and on_post()
for adding a new task.
TaskResource
定义了两种方法, on_get()
用于获取所有记录的任务,以及 on_post()
用于添加新任务。
(Create URLs)
URLs provide a way for the client to identify resources uniquely.
URL为客户端提供了一种唯一标识资源的方法。
Edit the main.py
file to include the URL for the TaskResource
as follows:
编辑main.py
文件以包括网址TaskResource
如下:
from app import views
application.add_route(‘/tasks/’, views.TaskResource())
Now, when a request comes in for /tasks
, Falcon will call the responder on the task’s resource that corresponds to the requested HTTP method.
现在,当请求/tasks
,Falcon将在与请求的HTTP方法相对应的任务资源上调用响应者。
The project’s structure should now look like this:
该项目的结构现在应如下所示:
Navigate to http://localhost:8000/tasks
and see your resource in action.
导航到http://localhost:8000/tasks
并查看正在使用的资源。
(Add a task)
(View tasks)
(Hooks)
Hooks in Falcon are similar to Python decorators.
Falcon中的钩子类似于Python装饰器。
Suppose our application required the user to be logged in before they could add or view tasks. We would implement this with a before
hook.
假设我们的应用程序要求用户先登录,然后才能添加或查看任务。 我们将使用一个before
钩子实现它。
Let’s create a hook that will run before each request to view or add a task.
让我们创建一个挂钩,该挂钩将在每个查看或添加任务的请求之前运行。
Next, we attach the hook to the on_post()
responder and on_get()
responders, as shown below.
接下来,我们将钩子附加到on_post()
响应程序和on_get()
响应者,如下所示。
class TaskResource(BaseResource):
@falcon.before(login_required)
def on_get(self, req, resp):...
@falcon.before(login_required)
def on_post(self, req, resp):...
Now, before every call to that responder, Falcon will first invoke login_required
, giving the following response.
现在,在每次调用该响应者之前,Falcon将首先调用login_required
,并给出以下响应。
{
“title”: “401 Unauthorized”
}
(Error handling)
Falcon provides a set of error classes you can raise when something goes wrong, hence making it easier to detect and log errors.
Falcon提供了一组错误类别,您可以在出现问题时引发这些错误类别,从而使其更易于检测和记录错误。
These error classes include:
这些错误类别包括:
falcon.HTTPBadRequest
falcon.HTTPBadRequest
falcon.HTTPInvalidHeader
falcon.HTTPInvalidHeader
HTTPMissingHeader
HTTPMissingHeader
HTTPInvalidParam
HTTPInvalidParam
We have already been able to use the falcon.HTTPUnauthorized
error in the login_required
hook.
我们已经能够在login_required
挂钩中使用falcon.HTTPUnauthorized
错误。
(Conclusion)
This tutorial has covered most of what is required to be able to build a REST API with Falcon successfully. For more information, see Falcon docs.
翻译自: https://medium.com/better-programming/an-introduction-to-the-falcon-framework-a787ceea098