-
[
Auth0 文档
在几分钟内实现认证](auth0.com/docs)
Python是我最喜欢的编程语言。它的适应性、可读性和编码速度都很独特,使Python成为各种项目的有力选择,从数据科学项目到脚本,当然也包括API。
Python是API开发的热门选择,不仅因为它是最受喜爱的编程语言之一,还因为它有丰富的库和框架生态系统为这个目标服务,这些库有巨大的知名度,如Django、Flask和FastAPI。
但是你应该用哪个框架来构建你的Python的API?这100%取决于你,但有一些重要的考虑因素需要记住。毕竟,这些框架中的一些是不同的,甚至是来自于意识形态的不同。
- Django是一个包罗万象的框架。它提供了处理API请求、序列化、数据库连接、自动生成管理界面等的工具和模块。
- 相反,Flask是一个简约的框架,它只提供必要的工具,但它通过额外的库和框架来扩展其功能。最重要的是,你完全可以决定你的项目需要什么,仅此而已。
- FastAPI是一个相对较新的框架。它利用了较新的python特性,如类型提示、并发处理(有async),而且速度超快。
我经常使用Flask和FastAPI,我很喜欢这两者。我喜欢这些框架的灵活性和适应性,在今天的文章中,我们将重点讨论Flask。
下面的提示和实践是研究的结果,也是八年多来用Python构建和运送生产级API的经验。
-
用正确的名称和HTTP动词设计你的API端点
-
如何正确构建你的应用程序
-
从代码中构建你的文档
-
测试
让我们开始吧!🚀
用正确的名称和HTTP动词设计你的API端点
一个充分设计的API对于开发者来说是很容易理解的,也是很直接的。通过阅读URI和HTTP动词(后面会有更多介绍),开发人员可以很好地理解在调用某个特定方法时应该发生什么。
但这是如何实现的呢?让我们从命名URI开始。在REST中,我们把Resource ,以第一层的数据表示。在你的API中一致地命名这些资源将变成长期的最佳决定之一。
请注意,我在前一句话中强调了一致性,因为这是一个关键因素。当然,有一些特别的方法来命名你的资源,我们将介绍这些方法,但对于你选择的实际约定来说,一致是更重要的。
让我们通过模拟一个有客户、订单和结账过程的简单电子商务网站来开始实践。
我们的主要资源是customers ,它是实例customer 的一个集合。有了这些信息,我们可以通过URI来识别集合资源 /customers或通过URI来识别单个资源 /customers/{customerId}.随后,我们可以识别子资源,如orders ,我们可以将其识别为 /customers/{customerId}/orders,或者通过一个单一的订单资源 /customers/{customerId}/orders/{orderId}.
命名资源的最佳实践
- 使用名词的复数形式来表示资源,例如:使用"-"来表示。
- ✅一个系统的用户。
/users,/users/{userId} - ✅ 用户的播放列表。
/users/{userId}/playlists,/users/{userId}/playlists/{playlistId}
- ✅一个系统的用户。
- 使用连字符"-"来分隔单词并提高可兑换性。
- ✅
/users/{userId}/-mobile-devices - ❌
/users/{userId}/mobileDevices - ❌
/users/{userId}/mobile_devices
- ✅
- 使用正斜线"/"来表示层次结构
- ✅
/users/{userId}/mobile-devices - ❌
/users-mobile-devices/{userId} - ❌
/users-mobile-devices/?userId={userId}
- ✅
- 在URI中只使用小写字母
- ✅
/users/{userId}/mobile-devices - ❌
/Users/{userId}/Mobile-Devices
- ✅
现在我们了解了如何命名资源,我们需要考虑行动。在我们的API中,有一些方法本质上是程序性的,与特定的资源无关,例如:结账、运行、播放等。
命名行动的最佳做法
- 使用动词来表示行动,例如。
- ✅ 执行一个结账动作。
/users/{userId}/cart/checkout
- ✅ 执行一个结账动作。
- 与资源一样,使用连字符、正斜线和小写字母。
这里的一个关键点是要区分CRUD函数和动作,因为两者都是动作。在REST中,CRUD操作,如创建、读取、更新和删除,是通过HTTP动词而不是URI来处理的。
但什么是HTTP动词或HTTP请求方法?
HTTP定义了一组请求方法,以表示对资源进行的操作(听起来很熟悉吧)。该列表包括几个,但我们将专注于5个。
- GET:应该是用于数据检索的。
- POST:应该用来创建一个新的资源。
- PUT:应该用于更新特定资源的信息。
- DELETE:应该用来删除一个特定的资源。
- PATCH:应该用于更新特定资源的部分信息。
我们的电子商务网站的例子
- ✅GET
/users:所有用户的列表。 - ✅POST
/users:创建一个新的用户。 - ✅PUT
/users/{userId}: 更新一个用户。 - ✅DELETE
/users/{userId}:删除一个特定的用户。 - ✅PATCH
/users/{userId}:部分地更新一个用户。 - ✅GET
/users/{userId}/orders列表:某个特定用户的所有订单。 - ✅POST
/users/{userId}/cart/checkout:运行结账过程。
你不应该做什么。
- ❌
/users/get-all - ❌
/users/create - ❌
/users/{userId}/list-orders
以任何形式的GET、POST或其他动词。
如何正确地构建你的应用程序
我想在这一节开始说,没有一个正确的方法来结构你的应用程序,这取决于应用程序的大小、模块、要求、甚至个人的喜好。这可能会有所不同。不过,我想向你介绍一下我的团队是如何结构Flask应用程序的,我们在多个生产项目中使用了这种设置。
你可以按照文章中对结构的解释,你也可以在github上的Flask API入门套件中找到这个结构,可以随时使用。
project/
api/
model/
__init__.py
welcome.py
route/
home.py
schema/
__init__.py
welcome.py
service
__init__.py
welcome.py
test/
route/
__init__.py
test_home.py
__init.py
.gitignore
app.py
Pipfile
Pipfile.lock
现在让我们把它分解开来,解释每个模块。
所有的应用魔法都发生在API模块(/api),在那里,我们把代码分成4个主要部分。
models是我们应用程序的数据描述符,在很多情况下与数据库模型有关。每个模型如何定义将在很大程度上取决于你用来连接数据库的库。routes是我们应用程序的URI,我们在这里定义我们的资源和动作。schemas是对我们的API的输入和输出的定义,允许哪些参数,我们将输出哪些信息。它们与我们的资源相关联,但它们不一定与我们的模型相同。services是定义应用逻辑或与其他服务或db层交互的模块。路由应该尽可能的简单,并将所有的逻辑委托给服务。
Flask中的每个端点都可以单独定义,也可以通过称为蓝图的组来定义。在我的例子中,我喜欢蓝图提供的分组,我为每个资源使用它们。让我们来看看我们的欢迎路线的一个例子 (./api/route/home.py)会是什么样子。
from http import HTTPStatus
from flask import Blueprint
from flasgger import swag_from
from api.model.welcome import WelcomeModel
from api.schema.welcome import WelcomeSchema
home_api = Blueprint('api', __name__)
@home_api.route('/')
@swag_from({
'responses': {
HTTPStatus.OK.value: {
'description': 'Welcome to the Flask Starter Kit',
'schema': WelcomeSchema
}
}
})
def welcome():
"""
1 liner about the route
A more detailed description of the endpoint
---
"""
result = WelcomeModel()
return WelcomeSchema().dump(result), 200
让我们把所有的东西分成3块。
home_api = Blueprint('api', __name__)
这里是我们声明蓝图的地方,随后我们可以用它来声明我们的端点或路由。在这种情况下,我们的分组是非常基本的,但我们可以用分组做得更多,比如定义前缀、资源文件夹等等。
例如,如果我们想让我们的home 蓝图总是作为一个嵌套的路由的 /home-service,我们可以这样做。
home_api = Blueprint('api', __name__, url_prefix='/home-service')
接下来我们声明一个路由,但我们把它分成两部分。
@home_api.route('/')
@swag_from({
'responses': {
HTTPStatus.OK.value: {
'description': 'Welcome to the Flask Starter Kit',
'schema': WelcomeSchema
}
}
})
我们在函数之上使用注解,将其转换为端点,并提供额外的信息,例如,文档信息,下一节会有更多介绍。
最后是我们的路由代码,它只是一个Python函数。
def welcome():
"""
1 liner about the route
A more detailed description of the endpoint
---
"""
result = WelcomeModel()
return WelcomeSchema().dump(result), 200
注意,我们不是简单地直接返回一个字符串或JSON对象,而是使用我们的模式。在我们的例子中,我使用flask-marshmallow序列化库来实现其目的。
从代码中构建你的文档
你建立了你的API,你把它运到了生产中,开发人员渴望使用它,但他们如何知道有哪些端点可用,如何使用它们?简单的答案就是阅读文档。
文档可以通过两种方式建立,你可以打开一个编辑器,"手动 "编写文档,或者你可以使用代码来生成文档。如果你喜欢自动文档的想法,你会喜欢Swagger。
Swagger是一个开源的规范,它允许你描述你的API的每个元素,这样任何机器或系统都可以解释它并与之互动。由于这个规范,许多工具被开发出来,以提供丰富的接口,使我们的文档变得动态和互动,同时也为开发人员提供了轻松生成这些swagger文件的工具。
对于Flask来说,有多个用于自动生成Swagger的库,但我最喜欢的是flasgger。Flassger提供了注释和其他工具来生成你的文档,它还提供了一个漂亮的网络界面,你可以看到每个端点、它的输入和输出,甚至可以直接从文档中运行端点。
下面是它的一张运行图片。
它是高度可配置的,并且通过使用一个额外的叫做apispec的库与我们的序列化库兼容。这一切都很容易设置,但你也可以利用Flask的入门套件,你将为你完成这一切。
但是,一旦你有了它的运行,那么文档的信息取自哪里呢?来自两个地方。
-
还记得我们的 swag_from 函数注解吗?在那里我们可以提供关于输入和输出的详细信息
@swag_from({ 'responses': { HTTPStatus.OK.value: { 'description': 'Welcome to the Flask Starter Kit', 'schema': WelcomeSchema } } }) -
我们还可以在函数中使用字符串字面,为端点提供描述,类似于我们在这里做的。
def welcome(): """ 1 liner about the route A more detailed description of the endpoint --- """
还有更多的选项和定制;在他们的官方文档中都有很好的记录。
测试
如果你像我一样,也许你讨厌写测试,但如果你像我一样,你知道这是值得的。测试,如果做得好的话,从长远来看可以提高效率和质量。在对现有系统进行修改、重构或构建新功能时,它们还能让开发人员放心。
建立测试不应该太难,它应该在开发过程中自然发生。我过去在这方面做了很多努力,因为我总是先开发功能、端点或函数,然后再写测试,只是为了完成它。
我并不是说这种方法是错误的,但有一种更好的方法。TDD,即测试驱动开发,这是一个概念,你先写测试,然后再写我们要测试的实际代码。
它是如何工作的呢?让我们假设我们需要写一个函数,将两个数字相加并返回结果;令人兴奋,对吗?
通过TDD,我们的方法是先写测试。
def test_answer():
assert sum_two_numbers(3, 5) == 8
接下来,我们运行测试,结果失败了,因为我们的函数甚至还不存在。所以接下来,我们写我们的函数。
def sum_two_numbers(num1, num2):
return num1 * num2
接下来,我们重新运行我们的测试,但还是失败了。我们的断言失败了,但是为什么?事实证明,我犯了一个简单的错误。像我这样笨拙的人,我放了一个*,而不是一个+;如果没有我们的测试,就很难注意到这一点,但感谢上帝,我们有测试。
我们修复了我们的函数,现在一切都运行得很好。
def sum_two_numbers(num1, num2):
return num1 + num2
在我们所做的练习中,这听起来有点傻,但对于更复杂的函数和代码来说,错误是会发生的,先有测试会有很大的帮助;我这么说是有经验的。
结论
对于不同的框架、要解决的问题、甚至人来说,最佳实践可能是不同的,没有一个正确的方法,这是我喜欢的编程方式。然而,在设计和开发API时,有基本的原则可以依靠,可以帮助你的团队和其他开发者消费你的API产品。
在命名上保持一致,在项目中的模块或文件夹中分离概念,直接从你的代码中编写文档,以及适当的测试,这些只是一些例子,可以使你的生活更容易,更有成效,并使你达到更高的水平。
我希望你喜欢阅读这篇文章!
-
博客
-
公司介绍
-
产品
-
更多
© 2013-2021 Auth0公司。保留所有权利。