所有文章 > API使用场景 > Python和REST APIs:与Web服务的交互
Python和REST APIs:与Web服务的交互

Python和REST APIs:与Web服务的交互

网上有大量可用的数据。许多 网络服务(如 YouTube 和 GitHub)通过应用程序编程接口API向第三方应用程序提供数据访问权限。构建 API 的最流行方法之一是 REST 架构样式。Python 提供了一些很棒的工具,不仅可以从 REST API 获取数据,还可以构建您自己的 Python REST API。

在本教程中,您将学习:

  • 什么是 REST 架构
  • REST API 如何提供对 Web 数据的访问
  • 如何通过 requests 库使用 REST API 中的数据
  • 构建 REST API 需要采取哪些步骤
  • 用于构建 REST API 的一些常用 Python 工具

通过使用 Python 和 REST API,您可以检索、解析、更新和操作您感兴趣的任何 Web 服务提供的数据。

REST 架构

REST 代表 representational state transfer,是一种软件架构风格,它定义了客户端和服务器通过网络进行通信的模式。REST 为软件架构提供了一组约束,以提高系统的性能、可扩展性、简单性和可靠性。

REST 定义了以下架构约束:

  • 无状态:服务器不会在来自客户端的请求之间维护任何状态。
  • 客户端-服务器:客户端和服务端必须相互解耦,允许各自独立开发。
  • 可缓存:从服务器检索到的数据应可由客户端或服务器缓存。
  • 统一接口:服务器将提供一个统一的接口来访问资源,而无需定义它们的表示形式。
  • 分层系统:客户端可以通过其他层(如代理或负载均衡器)间接访问服务器上的资源。
  • 按需编码(可选):服务器可以将代码传输到它可以运行的客户端,例如用于单页应用程序的 JavaScript。

请注意,REST不是一项具体规范,而是关于如何构建网络连接软件系统的一组指导原则。

REST API 和 Web 服务

REST Web 服务是遵守 REST 体系结构约束的任何 Web 服务。这些 Web 服务通过 API 向外界公开其数据。REST API 通过公共 Web URL 提供对 Web 服务数据的访问。

例如,以下是GitHub REST API的一个URL:

https://api.github.com/users/<username>

此 URL 允许您访问有关特定 GitHub 用户的信息。您可以通过向特定 URL 发送 HTTP 请求并处理响应来访问 REST API 中的数据。

HTTP 方法

REST API监听诸如GET、POST和DELETE等HTTP方法,以了解要对Web服务的资源执行哪些操作。资源是Web服务中可以通过HTTP请求访问和操作的任何数据。HTTP方法告诉API对资源执行什么操作。

虽然有许多 HTTP 方法,但下面列出的五种方法是 REST API 最常用的方法:

HTTP 方法描述
GET检索现有资源。
POST创建新资源。
PUT更新现有资源。
PATCH部分更新现有资源。
DELETE删除资源。

REST API 客户端应用程序可以使用这五种 HTTP 方法来管理 Web 服务中的资源状态。

状态代码

一旦 REST API 收到并处理 HTTP 请求,它将返回 HTTP 响应。此响应中包括 HTTP 状态代码。此代码提供有关请求结果的信息。发送请求到API的应用程序可以检查状态码并根据结果执行操作。这些操作可能包括处理错误或向用户显示成功消息。

以下是 REST API 返回的最常见状态代码列表:

法典意义描述
200还行请求的操作成功。
201创建已创建新资源。
202接受已收到请求,但尚未进行任何修改。
204无内容请求成功,但响应没有内容。
400错误请求请求格式不正确。
401未经 授权客户端无权执行请求的操作。
404未找到找不到请求的资源。
415不支持的媒体类型服务器不支持请求数据格式。
422Unprocessable Entity请求数据的格式正确,但包含无效或缺失的数据。
500内部服务器错误服务器在处理请求时引发错误。

这 10 个状态代码仅代表可用 HTTP 状态代码的一小部分。状态代码根据结果的类别进行编号:

代码范围类别
2xx成功运营
3xx重定向
4xx客户端错误
5xx服务器错误

在使用 REST API 时,HTTP状态码非常方便,因为您通常需要根据请求的结果执行不同的逻辑。

API 端点

REST API公开一组公共URL,客户端应用程序可以使用这些 URL 来访问 Web 服务的资源。这些 URL 在 API 的上下文中称为端点

为了帮助澄清这一点,请查看下表。在此表中,您将看到假设的 CRM 系统的 API 终端节点。这些端点用于代表系统中潜在客户的客户资源:

HTTP 方法API 终端节点描述
GET/customers获取客户列表。
GET/customers/<customer_id>获得单个客户。
POST/customers创建新客户。
PUT/customers/<customer_id>更新客户。
PATCH/customers/<customer_id>部分更新客户。
DELETE/customers/<customer_id>删除客户。

上述每个终端节点都根据 HTTP 方法执行不同的操作。

注意:为简洁起见,端点的基本URL已省略。实际上,您需要完整的 URL 路径才能访问 API 端点:

https://api.example.com/customers

这是您用来访问该端点的完整URL。基本URL是除了/customers之外的所有内容。

您会注意到,某些终端节点的末尾有<customer_id>。这个符号意味着您需要将一个数字的customer_id附加到URL中,以告诉REST API您想要处理哪个客户。

上面列出的端点只代表系统中的一种资源。生产就绪的 REST API 通常有数十甚至数百个不同的端点来管理 Web 服务中的资源。

REST 和 Python:使用 API

为了编写与 REST API 交互的代码,大多数Python开发人员都会使用requests库来发送HTTP请求。该库抽象化了进行HTTP请求的复杂性。它是少数几个值得作为标准库一部分对待的项目之一。

要开始使用requests,您需要先安装它。您可以使用 pip 来安装它:

$ python -m pip install requests

现在您已经安装了 requests,您可以开始发送 HTTP 请求。

GET

GET是使用 REST API 时最常用的 HTTP 方法之一。此方法允许您从给定的 API 中检索资源。 GET只读操作,所以您不应该用它来修改现有资源。

为了测试GET和其他方法,您将使用JSONPlaceholder 的服务。这项免费服务提供虚假的 API 终端节点,这些终端节点发回可处理的响应。

要尝试此操作,请启动 Python REPL 并运行以下命令以向 JSONPlaceholder 终端节点发送GET请求:

>>> import requests

>>> api_url = "https://jsonplaceholder.typicode.com/todos/1"

>>> response = requests.get(api_url)

>>> response.json()

{'userId': 1, 'id': 1, 'title': 'delectus aut autem', 'completed': False}

此代码调用requests.get()/todos/1发送 GET 请求,该请求使用 ID 为 todo1 的项目进行响应。然后,您可以对对象调用 .json() 以查看从 API 返回的数据。

响应数据以JSON格式格式化,类似于Python字典的键值存储。这是一种非常流行的数据格式,是大多数REST API的事实交换格式。

除了从 API 查看 JSON 数据之外,您还可以查看有关 :response

>>> response.status_code 200

>>> response.headers["Content-Type"] 'application/json; charset=utf-8'

在这里,您访问response.status_code来查看HTTP状态码。您还可以通过response.headers查看响应的HTTP头部。这个字典包含有关响应的元数据,例如响应的内容类型。

POST

现在,看看如何使用 requests 到 REST API 来创建新资源。您将再次使用 JSONPlaceholder,但这次您将在请求中包含 JSON 数据。以下是您将发送的数据:

{

"userId": 1,

"title":

"Buy milk",

"completed": false

}

此JSON包含一个新待办事项的信息。返回 Python REPL,运行以下代码以创建新的待办事项:

>>> import requests

>>> api_url = "https://jsonplaceholder.typicode.com/todos"

>>> todo = {"userId": 1, "title": "Buy milk", "completed": False}

>>> response = requests.post(api_url, json=todo)

>>> response.json() {'userId': 1, 'title': 'Buy milk', 'completed': False, 'id': 201}

>>> response.status_code 201

这里,您调用requests.post()在系统中创建一个新的待办事项。

首先,您创建一个包含待办事项数据的字典。然后,您将此字典传递给requests.post()的json关键字参数。当您这样做时,requests.post()会自动将请求的HTTP头部Content-Type设置为application/json。它还序列化todo成JSON字符串,将其附加到请求的主体中。

如果您不使用 keyword 参数来提供 JSON 数据,则需要相应地设置Content-Type并手动序列化 JSON。以下是与之前代码等效的版本:

>>> import requests

>>> import json

>>> api_url = "https://jsonplaceholder.typicode.com/todos"

>>> todo = {"userId": 1, "title": "Buy milk", "completed": False}

>>> headers = {"Content-Type":"application/json"}

>>> response = requests.post(api_url, data=json.dumps(todo), headers=headers)

>>> response.json() {'userId': 1, 'title': 'Buy milk', 'completed': False, 'id': 201}

>>> response.status_code 201

在此代码中,您将添加一个包含单个头部Content-Type字典,其中包含一个设置为application/json . 这告诉REST API您正在发送JSON数据。

然后您调用requests.post(),而不是将todo传递给json参数,而是首先调用json.dumps(todo)对其进行序列化。序列化后,您将其传递给data关键字参数。data参数告诉requests要在请求中包含什么数据。您还将头部字典传递给requests.post()以手动设置HTTP头部。

当您像这样调用requests.post()时,它与前面的代码具有相同的效果,但为您提供了更多对请求的控制。

注意:json.dumps() 来自标准库中的 json 包。这个包提供了在Python中处理JSON的有用方法。

API 响应后,您调用 response.json()以查看 JSON。JSON包括了新待办事项的生成ID。201状态码告诉你创建了一个新资源。

PUT

除了GET和POST,requests还提供了你在REST API中使用的所有其他HTTP方法的支持。以下代码发送一个PUT请求,用新数据更新现有的待办事项。任何与PUT请求一起发送的数据将完全替换待办事项的现有值。

你将使用与GET和POST相同的JSONPlaceholder端点,但这次你在URL末尾追加了10。这告诉REST API你想要更新哪个待办事项:

>>> import requests

>>> api_url = "https://jsonplaceholder.typicode.com/todos/10"

>>> response = requests.get(api_url)

>>> response.json() {'userId': 1, 'id': 10, 'title': 'illo est ... aut', 'completed': True}

>>> todo = {"userId": 1, "title": "Wash car", "completed": True}

>>> response = requests.put(api_url, json=todo)

>>> response.json() {'userId': 1, 'title': 'Wash car', 'completed': True, 'id': 10}

>>> response.status_code 200

在这里,你首先调用requests.get()来查看现有待办事项的内容。接下来,你用新的JSON数据调用requests.put()来替换现有的待办事项的值。当你调用response.json()时,可以看到新的值。成功的PUT请求总是返回200而不是201,因为你没有创建新资源,只是更新了现有资源。

PATCH

接下来,你将使用requests.patch()来修改现有待办事项上特定字段的值。PATCH与PUT不同,它不会完全替换现有资源。它只修改请求中发送的JSON中设置的值。

你将使用上一个例子中的相同待办事项来尝试requests.patch() 。以下是当前值:

{'userId': 1, 'title': 'Wash car', 'completed': True, 'id': 10}

现在你可以用一个新值更新标题:

>>> import requests

>>> api_url = "https://jsonplaceholder.typicode.com/todos/10"

>>> todo = {"title": "Mow lawn"}

>>> response = requests.patch(api_url, json=todo)

>>> response.json() {'userId': 1, 'id': 10, 'title': 'Mow lawn', 'completed': True}

>>> response.status_code 200

当你调用response.json()时,可以看到标题已更新为“Mow lawn”。

DELETE

最后但同样重要的是,如果你想完全删除一个资源,那么你使用DELETE。以下是删除待办事项的代码:

>>> import requests

>>> api_url = "https://jsonplaceholder.typicode.com/todos/10"

>>> response = requests.delete(api_url)

>>> response.json() {}

>>> response.status_code 200

你调用requests.delete()并传入包含你想要删除的待办事项ID的API URL。这将向REST API发送一个DELETE请求,然后删除匹配的资源。在删除资源后,API会发回一个空的JSON对象,表示资源已被删除。

requests库是处理REST API的一个极好工具,也是 Python 工具带中不可或缺的一部分。在下一节中,你将改变方向,考虑构建REST API所需的步骤。

REST 和 Python:构建 API

REST API设计是一个涉及许多层次的巨大主题。与技术中的大多数事物一样,关于构建 API 的最佳方法存在广泛的意见。在本节中,你将查看一些建议的步骤,以便你在构建API时遵循。

识别资源

构建 REST API 时,您将采取的第一步是确定 API 将管理的资源。通常将这些资源描述为复数名词,如客户、事件或交易。当你在Web服务中识别不同的资源时,你将构建一个描述用户在API中可以管理的不同数据的名词列表。

在进行此操作时,确保考虑任何嵌套资源。例如,客户可能有销售记录,或者事件可能包含嘉宾。在定义 API 终端节点时,建立这些资源层次结构会有所帮助。

定义您的终端节点

一旦你识别了Web服务中的资源,你将使用这些资源来定义API端点。以下是你可能在支付处理服务的API中找到一个交易资源的一些示例端点:

HTTP 方法API 终端节点描述
GET/transactions获取交易列表。
GET/transactions/<transaction_id>获取单笔交易。
POST/transactions创建新交易。
PUT/transactions/<transaction_id>更新交易。
PATCH/transactions/<transaction_id>部分更新事务。
DELETE/transactions/<transaction_id>删除交易。

这 6 个终端节点涵盖了您需要在 Web 服务中创建、读取、更新和删除的所有操作。你的Web服务中的每个资源都会有一个基于用户可以使用API执行的操作的类似端点列表。

注意:终端节点不应包含动词。相反,您应该选择适当的 HTTP 方法来传达终端节点的操作。例如,下面的端点包含一个不需要的动词:

GET /getTransactions

在这里,”get” 被包含在端点中,而这是不需要的。HTTP方法GET已经通过指示动作提供了端点的语义意义。你可以从端点中移除 “get”:

GET /transactions

此终端节点仅包含一个复数名词,HTTP 方法传达 GET 操作。

现在看看一个嵌套资源的端点示例。这里,你会看到嵌套在events资源下的guests的端点:

HTTP 方法API 终端节点描述
GET/events/<event_id>/guests获取来宾列表。
GET/events/<event_id>/guests/<guest_id>获取单个来宾。
POST/events/<event_id>/guests创建新来宾。
PUT/events/<event_id>/guests/<guest_id>更新 guest。
PATCH/events/<event_id>/guests/<guest_id>部分更新 guest。
DELETE/events/<event_id>/guests/<guest_id>删除来宾。

使用这些端点,你可以在系统中管理特定事件的客人。

这不是定义嵌套资源端点的唯一方式。有些人更喜欢使用查询字符串来访问嵌套资源。查询字符串允许你通过HTTP请求发送额外的参数。在下面的端点中,你将查询字符串附加到获取特定event_id的guests:

GET /guests?event_id=23

这个端点将过滤掉任何没有引用给定event_id的guests。与API设计中的许多事情一样,你需要决定哪种方法最适合你的Web服务。

注意:你的REST API在整个Web服务生命周期内保持相同是非常不可能的。资源会发生变化,你需要更新端点以反映这些变化。这就是API版本化发挥作用的地方。

有多种版本控制策略。选择正确的选项取决于您的 API 的要求。以下是一些最受欢迎的 API 版本控制选项:

  • URI 版本控制
  • HTTP 标头版本控制
  • 查询字符串版本控制
  • 媒体类型版本控制

无论你选择什么策略,对你的API进行版本化是确保它能够适应不断变化的需求同时支持现有用户的重要步骤。

既然你已经了解了端点,下一节将探讨在你的REST API中格式化数据的一些选项。

选择您的数据交换格式

两种流行的用于格式化Web服务数据的选项是XML和JSON。传统上,XML在SOAP API中非常流行,但JSON在REST API中更受欢迎。为了比较这两者,请看一个以XML和JSON格式化的书籍资源示例。

以下是以XML格式化的书籍:

<?xml version="1.0" encoding="UTF-8" ?>

<book>

<title>Python Basics</title>

<page_count>635</page_count>

<pub_date>2021-03-16</pub_date>

<authors>

<author>

<name>David Amos</name>

</author>

<author>

<name>Joanna Jablonski</name>

</author>

<author>

<name>Dan Bader</name>

</author>

<author>

<name>Fletcher Heisler</name>

</author>

</authors

> <isbn13>978-1775093329</isbn13>

<genre>Education</genre>

</book>

XML 使用一系列元素对数据进行编码。每个元素都有一个开始和结束标签,数据位于两者之间。元素可以嵌套在其他元素中。如上所示,几个<author>标签嵌套在<authors>内部。

现在,看看同一本书的JSON表示:

{

"title": "Python Basics",

"page_count": 635,

"pub_date": "2021-03-16",

"authors": [

{"name": "David Amos"},

{"name": "Joanna Jablonski"},

{"name": "Dan Bader"},

{"name": "Fletcher Heisler"}

],

"isbn13": "978-1775093329",

"genre": "Education"

}

JSON以键值对的形式存储数据,类似于Python字典。像XML一样,JSON支持任何级别的数据嵌套,因此你可以建模复杂的数据。

JSON和XML本身没有优劣之分,但在REST API开发者中,更倾向于使用JSON。尤其是当你将REST API与前端框架(如React或Vue)配对使用时。

设计成功响应

一旦选择了数据格式,下一步是确定如何响应 HTTP 请求。所有来自你的REST API的响应应该具有类似的格式,并包括适当的HTTP状态码。

在本节中,你将查看一个假设API管理汽车库存的示例HTTP响应。这些例子将给你一种感觉,你应该如何格式化API响应。为了让事情清晰,我们将查看原始HTTP请求和响应,而不是使用HTTP库,如requests。

首先,看看对/cars的GET请求,它返回一列carscars

GET /cars HTTP/1.1

Host: api.example.com

此 HTTP 请求由四个部分组成:

  1. GET 是 HTTP 方法类型。
  2. /cars 是 API 端点。
  3. HTTP/1.1 是 HTTP 版本。
  4. Host:api.example.com 是 API 主机。

这四部分是你向/cars发送GET请求所需的全部信息。现在看看响应。这个API使用JSON作为数据交换格式:

HTTP/1.1 200 OK

Content-Type: application/json

...

[

{

"id": 1,

"make": "GMC",

"model": "1500 Club Coupe",

"year": 1998,

"vin": "1D7RV1GTXAS806941",

"color": "Red"

},

{

"id": 2,

"make": "Lamborghini",

"model":"Gallardo",

"year":2006,

"vin":"JN1BY1PR0FM736887",

"color":"Mauve"

},

{

"id": 3,

"make": "Chevrolet",

"model":"Monte Carlo",

"year":1996,

"vin":"1G4HP54K714224234",

"color":"Violet"

}

]

API 返回一个cars响应,其中包含 200 OK .您知道响应成功是成功的。响应还有一个Content-Type设置头部为application/json。这告诉用户将响应解析为JSON。

注意:当您使用真正的 API 时,您将看到比这更多的 HTTP 标头。这些标头因 API 而异,因此在这些示例中被排除了。

你还想在你的响应中包含适当的状态码。对于任何成功的GET请求,你应该返回200 OK。这告诉用户他们的请求已按预期处理。

看看另一个GET请求,这次是查询一辆汽车:

GET /cars/1 HTTP/1.1

Host: api.example.com

这个HTTP请求向API查询汽车1。以下是响应:

HTTP/1.1 200 OK

Content-Type: application/json

{

"id": 1,

"make": "GMC",

"model": "1500 Club Coupe",

"year": 1998,

"vin": "1D7RV1GTXAS806941",

"color": "Red"

},

此响应包含一个包含汽车数据的 JSON 对象。因为它是一个单独的对象,所以不需要包装在列表中。像上一个响应一样,这也有一个200 OK状态码。

注意:GET 请求绝不应修改现有资源。如果请求包含数据,则这些数据应被忽略,并且API应返回未更改的资源。

接下来,查看用于添加新汽车的POST请求:

POST /cars HTTP/1.1

Host: api.example.com

Content-Type: application/json

{

"make": "Nissan",

"model": "240SX",

"year": 1994,

"vin": "1N6AD0CU5AC961553",

"color": "Violet"

}

此POST请求在请求中包含了新车的JSON。它设置了内容类型头为application/json,以便API知道请求的内容类型。API将从JSON创建一辆新车。

以下是响应:

HTTP/1.1 201 Created

Content-Type: application/json

{

"id": 4,

"make": "Nissan",

"model": "240SX",

"year": 1994,

"vin": "1N6AD0CU5AC961553",

"color": "Violet"

}

这个响应有201 Created状态码,告诉用户创建了新资源。确保对所有成功的POST请求使用201 Created而不是200 OK。

此响应还包括由API生成的新车的副本,并带有id。发送响应时返回id很重要,以便用户可以再次修改资源。

注意:当用户用POST或PUT或PATCH创建资源时,始终要返回资源的副本。这样,用户就可以看到他们所做的更改。

现在看看PUT请求:

PUT /cars/4 HTTP/1.1

Host: api.example.com

Content-Type: application/json

{

"make": "Buick",

"model": "Lucerne",

"year": 2006,

"vin": "4T1BF3EK8AU335094",

"color":"Maroon"

}

此请求使用前一个请求的id来更新汽车的所有新数据)。提醒一下,PUT会用新数据更新资源的所有字段。以下是响应:

HTTP/1.1 200 OK

Content-Type: application/json

{

"id": 4,

"make": "Buick",

"model": "Lucerne",

"year": 2006,

"vin": "4T1BF3EK8AU335094",

"color":"Maroon"

}

响应包括带有新数据的汽车的副本。同样,对于PUT请求,你总是想发送完整的资源。PATCH请求也是如此:

PATCH /cars/4 HTTP/1.1

Host: api.example.com

Content-Type: application/json

{

"vin": "VNKKTUD32FA050307",

"color": "Green"

}

PATCH请求只更新资源的一部分。在上面的请求中,vin和color字段将用新值更新。以下是响应:

HTTP/1.1 200 OK

Content-Type: application/json

{

"id": 4,

"make": "Buick",

"model": "Lucerne",

"year": 2006,

"vin": "VNKKTUD32FA050307",

"color": "Green"

}

响应包含汽车的完整副本。如你所见,只有vin和color字段已更新。

最后,看看当你的REST API收到DELETE请求时应如何响应。这是删除一辆车的DELETE请求:

DELETE /cars/4 HTTP/1.1

这个DELETE请求告诉API删除ID为4的汽车。以下是响应:

HTTP/1.1 204 No Content

这个响应只包括状态码204 No Content 。这个状态码告诉用户操作成功,但在响应中没有返回内容。这是因为汽车已被删除。没有理由在响应中发回它的副本。

上面的响应在一切顺利时效果很好,但如果请求出现问题怎么办?在下一节中,你将看到当发生错误时,你的REST API应该如何响应。

设计错误响应

对 REST API 的请求始终有可能失败。定义错误响应的样子是个好主意。 这些响应应该包括发生的错误描述以及适当的状态码。在本节中,您将查看几个示例。

首先,请查看对 API 中不存在的资源的请求:

GET /motorcycles HTTP/1.1

Host: api.example.com

在这里,用户发送GET请求到/motorcycles,这个路径在API中不存在。API返回以下响应:

HTTP/1.1 404 Not Found

Content-Type: application/json

...

{

"error": "The requested resource was not found."

}

此响应包括404 Not Found状态码。除此之外,响应还包含一个带有描述性错误消息的 JSON 对象。供描述性的错误信息可以为用户提供更多的错误上下文。

现在看一下用户发送无效请求时的错误响应:

POST /cars HTTP/1.1

Host: api.example.com

Content-Type: application/json

{

"make": "Nissan",

"year": 1994,

"color": "Violet"

此请求包含 JSON,但格式不正确。它在末尾缺少结束的花括号(})。API 将无法处理此数据。错误响应会告知用户问题:

HTTP/1.1 400 Bad Request

Content-Type: application/json

{

"error": "This request was not properly formatted. Please send again."

}

此响应包括描述性错误消息和400 Bad Request状态码,告诉用户需要修复请求。

即使请求格式正确,还有其他几种方式可能导致请求出错。在接下来的示例中,用户发送POST请求但包含了不支持的媒体类型:

POST /cars HTTP/1.1

Host: api.example.com

Content-Type: application/xml

<?xml version="1.0" encoding="UTF-8" ?>

<car>

<make>Nissan</make>

<model>240SX</model>

<year>1994</year>

<vin>1N6AD0CU5AC961553</vin>

<color>Violet</color>

</car>

在此请求中,用户发送 XML,但 API 仅支持 JSON。API 响应如下:

HTTP/1.1 415 Unsupported Media Type

Content-Type: application/json

{

"error": "The application/xml mediatype is not supported."

}

此响应包括415 Unsupported Media Type状态码,表示POST请求包含了API不支持的数据格式,这个错误码对于格式错误的数据是有意义的,但如果数据即使在正确的格式下也是无效的怎么办?

在下一个示例中,用户发送POST请求,但包含的汽车数据与其他数据的字段不匹配:

POST /cars HTTP/1.1

Host: api.example.com

Content-Type: application/json

{

"make": "Nissan",

"model": "240SX",

"topSpeed": 120

"warrantyLength": 10

}

在这个请求中,用户向JSON添加了topSpeed和warrantyLength字段。这些字段不受API支持,因此它用错误消息回应:

HTTP/1.1 422 Unprocessable Entity

Content-Type: application/json

{

"error": "Request had invalid or missing data."

}

此响应包括422 Unprocessable Entity状态码。此状态代码表示请求没有任何问题,但数据无效。REST API 需要验证传入数据。如果用户在请求中发送了数据,那么API应验证数据并通知用户任何错误。

响应请求,无论是成功的还是错误的,都是REST API最重要的任务之一。如果你的API直观并提供准确的响应,那么用户围绕你的网络服务构建应用程序会更容易。幸运的是,一些优秀的Python Web框架抽象化了处理HTTP请求和返回响应的复杂性。在下一节中,你将看到三个流行的选项。

REST 和 Python:交易工具

在本节中,你将看到三个流行的用于在Python中构建REST API的框架。每个框架都有优点和缺点,因此您必须评估哪个最适合您的需求。为此,在接下来的部分中,您将了解每个框架中的 REST API。所有示例都是针对管理国家集合的类似API。

每个国家/地区都将具有以下字段:

  • name是国家的名称。
  • capital是国家的首都。
  • area是国家的面积(以平方公里为单位)。

字段name、capital和area存储了世界上某个特定国家的数据。

大多数情况下,从 REST API 发送的数据来自数据库。连接到数据库不在本教程的讨论范围之内。对于以下示例,你将数据存储在一个Python列表中。Django REST框架示例的例外情况是,它运行在Django创建的SQLite数据库上。

注意:建议您为每个示例创建单独的文件夹以分隔源文件。您还需要使用虚拟环境来隔离依赖项。

为了保持一致,你将为所有三个框架使用国家作为主要端点。你还将使用JSON作为所有三个框架的数据格式。

现在您已经了解了 API 的背景,可以继续下一部分,您将在其中了解 Flask 中的 REST API。

Flask

Flask 是一个 Python 微框架,用于构建 Web 应用程序和 REST API。Flask 为您的应用程序提供了坚实的主干,同时留下了许多设计选择供你决定。Flask的主要工作是处理HTTP请求并将其路由到应用中的适当函数。

注意:本节中的代码使用新的 Flask 2 语法。如果你运行的是旧版本的 Flask,请使用 @app.route(“/countries”) 而不是 @app.get(“/countries”) 和 @app.post(“/countries”)。

要处理旧版本Flask中的POST请求,你还需要将methods参数添加到@app.route():

@app.route("/countries", methods=["POST"])

这个路由处理Flask 1中对/countries的POST请求。

下面是 REST API 的示例 Flask 应用程序:

# app.py

from flask import Flask, request, jsonify

app = Flask(__name__)

countries = [

{"id": 1, "name": "Thailand", "capital": "Bangkok", "area": 513120},

{"id": 2, "name": "Australia", "capital": "Canberra", "area": 7617930},

{"id": 3, "name": "Egypt", "capital": "Cairo", "area": 1010408},

]

def _find_next_id():

return max(country["id"] for country in countries) + 1

@app.get("/countries")

def get_countries():

return jsonify(countries)

@app.post("/countries")

def add_country():

if request.is_json:

country = request.get_json()

country["id"] = _find_next_id()

countries.append(country)

return country, 201

return {"error": "Request must be JSON"}, 415

此应用程序定义了API端点/countries以管理国家/地区列表。它处理两种不同类型的请求:

  1. GET /countries返回国家列表。
  2. POST /countries将新国家添加到列表中。

注意:此 Flask 应用程序包含仅处理对 API 终端节点的两种类型请求的函数,即 .在完整的 REST API 中,你会希望将其扩展到包括所有必需操作的函数。

你可以通过使用pip安装flask来尝试这个应用程序:

$ python -m pip install flask

安装后,将代码保存在名为app.py的文件中,要运行此Flask应用程序,首先需要设置一个名为FLASK_APP的环境变量为app.py。这告诉Flask哪个文件包含你的应用程序。

在包含app.py的文件夹中运行以下命令:

$ export FLASK_APP=app.py

这在当前shell中将FLASK_APP设置为app.py。或者,你可以将FLASK_ENV设置为development,这将使Flask处于调试模式:

$ export FLASK_ENV=development

除了提供有用的错误消息外,调试模式还将在所有代码更改后触发应用程序的重新加载。如果没有调试模式,每次更改后都需要重新启动服务器。

注意:上述命令适用于 macOS 或 Linux。如果您在 Windows 上运行它,则可以在命令提示符中这样设置FLASK_APP和FLASK_ENV:

C:\> set FLASK_APP=app.py

C:\> set FLASK_ENV=development

现在FLASK_APP和FLASK_ENV已经在Windows shell中设置好了。

环境变量准备就绪后,你现在可以通过调用flask run来启动Flask开发服务器:

$ flask run

* Serving Flask app "app.py" (lazy loading)

* Environment: development

* Debug mode: on

* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

这会启动一个运行应用程序的服务器。打开浏览器并访问http://127.0.0.1:5000/countries,你会看到以下响应:

[ 、

{

"area": 513120,

"capital": "Bangkok",

"id": 1,

"name": "Thailand"

},

{

"area": 7617930,

"capital": "Canberra",

"id": 2,

"name": "Australia"

},

{

"area": 1010408,

"capital": "Cairo",

"id": 3,

"name": "Egypt"

}

]

这个JSON响应包含了在app.py开始处定义的三个国家。查看以下代码以了解这是如何工作的:

@app.get("/countries")

def get_countries():

return jsonify(countries)

这段代码使用@app.get(),一个Flask路由装饰器,将GET请求连接到应用程序中的一个函数。当你访问/countries时,Flask调用被装饰的函数来处理HTTP请求并返回响应。

在上面的代码中,get_countries()获取countries(一个Python列表),并使用jsonify()将其转换为JSON。这个JSON是在响应中返回的。

注意:大多数时候,你可以直接从 Flask 函数返回一个 Python 字典。Flask 会自动将任何 Python 字典转换为 JSON。你可以在下面的函数中看到这一点:

@app.get("/country")

def get_country():

return countries[1]

在这段代码中,你返回了 countries 中的第二个字典。Flask 会将这个字典转换为 JSON。当你请求 /country 时,你将看到以下响应:

{

"area": 7617930,

"capital": "Canberra",

"id": 2,

"name": "Australia"

}

这是从 get_country() 返回的字典的 JSON 版本。

在 get_countries() 中,你需要使用 jsonify(),因为你返回的是字典列表,而不仅仅是单个字典。Flask 不会自动将列表转换为 JSON。

现在看看 add_country()。这个函数处理对 /countries 的 POST 请求,并允许你向列表中添加一个新国家。它使用 Flask 请求对象来获取有关当前 HTTP 请求的信息:

@app.post("/countries")

def add_country():

if request.is_json:

country = request.get_json()

country["id"] = _find_next_id()

countries.append(country)

return country, 201

return {"error": "Request must be JSON"}, 415

该函数主要负责以下操作:

  1. 使用 request.is_json 检查请求是否为 JSON
  2. 使用 request.get_json() 创建一个新的国家实例
  3. 找到下一个 id 并将其设置在国家上
  4. 将新国家添加到 countries
  5. 返回响应中的国家以及 201 Created 状态码
  6. 如果请求不是 JSON,则返回错误消息和 415 Unsupported Media Type 状态码

add_country() 还调用 _find_next_id() 来确定新国家的 id:

def _find_next_id():

return max(country["id"] for country in countries) + 1

此帮助程序函数使用生成器表达式选择所有国家/地区 ID,然后在它们上面调用 max() 以获取最大值。它将此值递增以获取要使用的下一个 ID。1

你可以在 shell 中使用命令行工具 curl 试用这个端点。在这里,你将把一个新国家添加到列表中:

$ curl -i http://127.0.0.1:5000/countries

-X POST

-H 'Content-Type: application/json'

-d '{"name":"Germany", "capital": "Berlin", "area": 357022}'

HTTP/1.0 201 CREATED

Content-Type: application/json

...

{

"area": 357022,

"capital": "Berlin",

"id": 4,

"name": "Germany"

}

此 curl 命令包含一些有助于了解的选项:

  • -X 设置请求的 HTTP 方法。
  • -H 将 HTTP 标头添加到请求中。
  • -d 定义请求数据。

使用这些选项,curl 通过将 Content-Type 标头设置为 application/json,以 JSON 格式发送带有 POST 请求的数据。REST API 返回 201 CREATED 以及你添加的新国家的 JSON。

注意:在这个例子中,add_country() 不包含任何验证,以确认请求中的 JSON 是否符合 countries 的格式。如果你希望验证 Flask 中 JSON 的格式,请查看 flask-expects-json。

你可以使用 curl 发送对 /countries 的 GET 请求,以确认新国家已添加。如果在 curl 命令中不使用 -X,则会默认发送 GET 请求:

$ curl -i http://127.0.0.1:5000/countries

HTTP/1.0 200 OK

Content-Type: application/json

...

[

{

"area": 513120,

"capital": "Bangkok",

"id": 1,

"name": "Thailand"

},

{

"area": 7617930,

"capital": "Canberra",

"id": 2,

"name": "Australia"

},

{

"area": 1010408,

"capital": "Cairo",

"id": 3,

"name": "Egypt"

},

{

"area": 357022,

"capital": "Berlin",

"id": 4,

"name": "Germany"

}

]

这将返回系统中国家/地区的完整列表,最新的国家/地区位于底部。

这只是Flask能做的一小部分示例。这个应用程序可以扩展,以包括所有其他HTTP方法的端点。Flask还有一个庞大的生态系统,为REST API提供了额外的功能,例如数据库集成、身份验证和后台处理。

Django REST 框架

另一种构建REST API的流行选项是Django REST框架。Django REST框架是一个Django插件,可以在现有的Django项目之上添加REST API功能。

要使用 Django REST 框架,你需要一个可以工作的Django项目。如果你已经有一个,那么你可以在你的项目中应用本节中的模式。否则,请继续阅读,你将构建一个Django项目并添加Django REST框架。

首先,使用pip安装Django和djangorestframework:

$ python -m pip install Django djangorestframework

这将安装Django和djangorestframework。现在你可以使用django-admin工具创建一个新的Django项目。运行以下命令开始你的项目:

$ django-admin startproject countryapi

此命令将在当前目录中创建一个名为countryapi的新文件夹。此文件夹中包含运行 Django 项目所需的所有文件。接下来,您将在项目中创建新的 Django 应用程序。Django 将项目的功能分解为应用程序。每个应用程序都管理项目的一个不同部分。

注意:在本教程中,你只会稍微了解Django的功能。如果您有兴趣了解更多信息,请查看可用的 Django 教程。

要创建应用程序,更改目录到countryapi并运行以下命令:

$ python manage.py startapp countries

这会在你的项目中创建一个名为countries的新文件夹。在这个文件夹中有该应用程序的基础文件。

现在你已经创建了一个可以工作的应用程序,你需要告诉Django关于它。在你刚刚创建的countries文件夹旁边是另一个名为countryapi的文件夹。这个文件夹包含你的项目的配置和设置。

注意:这个文件夹的名称与运行django-admin startproject countryapi时Django创建的根文件夹相同。

打开位于countryapi文件夹中的settings.py文件。向INSTALLED_APPS添加以下行以告诉Django关于countries应用程序和Django REST框架:

# countryapi/settings.py

INSTALLED_APPS = [

"django.contrib.admin",

"django.contrib.auth",

"django.contrib.contenttypes",

"django.contrib.sessions",

"django.contrib.messages",

"django.contrib.staticfiles",

"rest_framework", "countries",

]

你已经添加了countries应用程序和rest_framework的行。

您可能想知道为什么需要将 rest_framework 添加到应用程序列表中。您需要添加它,因为 Django REST 框架只是另一个 Django 应用程序。Django 插件是打包和分发的 Django 应用程序,任何人都可以使用。

下一步是创建一个 Django 模型来定义数据的字段。在countries应用程序中,使用以下代码更新models.py:

# countries/models.py

from django.db import models

class Country(models.Model):

name = models.CharField(max_length=100)

capital = models.CharField(max_length=100)

area = models.IntegerField(help_text="(in square kilometers)")

这段代码定义了一个Country模型。Django将使用此模型在数据库中创建国家数据的表和列。

运行以下命令,让 Django 基于此模型更新数据库:

$ python manage.py makemigrations

Migrations for 'countries':

countries/migrations/0001_initial.py

- Create model Country

$ python manage.py migrate

Operations to perform:

Apply all migrations: admin, auth, contenttypes, countries, sessions

Running migrations:

Applying contenttypes.0001_initial... OK

Applying auth.0001_initial... OK

...

这些命令使用 Django 迁移在数据库中创建新表。

这个表开始时为空,但为了测试Django REST框架,最好能有一些初始数据。为此,你将使用Django固定装置在数据库中加载一些数据。

将以下JSON数据复制并保存到一个名为countries.json的文件中,并将其保存在countries目录中:

[

{

"model": "countries.country",

"pk": 1,

"fields": {

"name": "Thailand",

"capital": "Bangkok",

"area": 513120

}

},

{

"model": "countries.country",

"pk": 2,

"fields": {

"name": "Australia",

"capital": "Canberra",

"area": 7617930

}

},

{

"model": "countries.country",

"pk": 3,

"fields": {

"name": "Egypt",

"capital": "Cairo",

"area": 1010408

}

}

]

此 JSON 包含三个国家/地区的数据库条目。调用以下命令将此数据加载到数据库中:

$ python manage.py loaddata countries.json

Installed 3 object(s) from 1 fixture(s)

这会向数据库添加三行记录。

至此,你的Django应用程序已经设置完毕,并填充了一些数据。现在你可以开始在项目中添加Django REST框架了。

Django REST框架会将现有的Django模型转换为JSON以供REST API使用。它通过模型序列化器来实现这一点。模型序列化器告诉Django REST框架如何将模型实例转换为JSON以及包括哪些数据。

你将为上面的Country模型创建序列化器。首先,在countries应用程序内创建一个名为serializers.py的文件。完成后,将以下代码添加到serializers.py中:

# countries/serializers.py

from rest_framework import serializers

from .models import Country

class CountrySerializer(serializers.ModelSerializer):

class Meta:

model = Country

fields = ["id", "name", "capital", "area"]

这个序列化器,CountrySerializer,继承自serializers.ModelSerializer,以根据Country的模型字段自动生成JSON内容。除非指定,否则ModelSerializer子类会在JSON中包含Django模型的所有字段。你可以通过将fields设置为你想要包含的数据列表来修改此行为。

就像 Django 一样,Django REST 框架使用视图从数据库中查询数据以显示给用户。你不需要从头编写REST API视图,可以继承Django REST框架的ModelViewSet类,它具有默认的常见REST API操作视图。

注意:Django REST 框架文档将这些视图称为 actions。

以下是ModelViewSet提供的动作及其对应的HTTP方法的列表:

HTTP 方法行动描述
GET.list()获取国家/地区列表。
GET.retrieve()获取单个国家/地区。
POST.create()创建一个新国家/地区。
PUT.update()更新国家/地区。
PATCH.partial_update()部分更新国家/地区。
DELETE.destroy()删除国家/地区。

如您所见,这些操作映射到您在 REST API 中期望的标准 HTTP 方法。您可以在子类中覆盖这些操作,也可以根据 API 的要求添加其他操作。

下面是名为CountryViewSet的ModelViewSet子类的代码。这个类将生成管理Country数据所需的视图。将以下代码添加到countries应用程序的views.py中:

# countries/views.py

from rest_framework import viewsets

from .models import Country

from .serializers import CountrySerializer

class CountryViewSet(viewsets.ModelViewSet):

serializer_class = CountrySerializer

queryset = Country.objects.all()

在这个类中,serializer_class设置为CountrySerializer,queryset设置为Country.objects.all()。这告诉Django REST框架使用哪个序列化器以及如何为这组视图查询数据库。

创建视图后,需要将它们映射到相应的 URL 或端点。为此,Django REST框架提供了一个DefaultRouter,它将为ModelViewSet自动生成URL。

在countries应用程序中创建一个urls.py文件,并将以下代码添加到文件中:

# countries/urls.py

from django.urls import path, include

from rest_framework.routers import DefaultRouter

from .views import CountryViewSet

router = DefaultRouter()

router.register(r"countries", CountryViewSet)

urlpatterns = [

path("", include(router.urls))

]

这段代码创建了一个DefaultRouter,并在countries URL下注册了CountryViewSet。这将把所有CountryViewSet的URL放在/countries/下面。

注意:Django REST框架会自动在任何由DefaultRouter生成的端点末尾添加一个正斜杠(/)。你可以像这样禁用这种行为:

router = DefaultRouter(trailing_slash=False)

这将禁用 endpoints 末尾的正斜杠。

最后,你需要更新项目的base urls.py文件以包含项目中所有的countries URLs。使用以下代码更新countryapi文件夹内的urls.py文件:

# countryapi/urls.py

from django.contrib import admin

from django.urls import path, include

urlpatterns = [

path("admin/", admin.site.urls),

path("", include("countries.urls")),

]

这将把所有URL放在/countries/下面。现在你可以尝试你的Django支持的REST API了。在root countryapi目录中运行以下命令以启动Django开发服务器:

$ python manage.py runserver

开发服务器现在正在运行。继续发送GET请求到/countries/以获取你的Django项目中所有国家的列表:

$ curl -i http://127.0.0.1:8000/countries/ -w

HTTP/1.1 200 OK

...

[

{

"id": 1,

"name":"Thailand",

"capital":"Bangkok",

"area":513120

},

{

"id": 2,

"name":"Australia",

"capital":"Canberra",

"area":7617930

},

{

"id": 3,

"name":"Egypt",

"capital":"Cairo",

"area":1010408

}

]

Django REST 框架发回包含您之前添加的三个国家/地区的 JSON 响应。上面的响应是为可读性而格式化的,因此您的响应看起来会有所不同。

你在countries/urls.py中创建的DefaultRouter为所有标准API端点提供URL:

  • GET /countries/
  • GET /countries/<country_id>/
  • POST /countries/
  • PUT /countries/<country_id>/
  • PATCH /countries/<country_id>/
  • DELETE /countries/<country_id>/

你可以在下面尝试更多端点。向/countries/发送POST请求以在你的Django项目中创建一个新Country:

$ curl -i http://127.0.0.1:8000/countries/

-X POST

-H 'Content-Type: application/json'

-d '{"name":"Germany", "capital": "Berlin", "area": 357022}'

-w '

'

HTTP/1.1 201 Created

...

{

"id":4,

"name":"Germany",

"capital":"Berlin",

"area":357022

}

这会在请求中发送的JSON中创建一个新Country。Django REST框架返回201 Created状态码和新Country。

注意:默认情况下,响应不会在末尾包含换行符。这意味着JSON可能会碰到你的命令提示符。上面的curl命令包括-w ‘ ‘以在JSON后添加一个新行字符来解决这个问题。

通过向GET /countries/<country_id>/发送请求并使用现有id可以查看现有Country。运行以下命令以获取第一个Country:idCountry

$ curl -i http://127.0.0.1:8000/countries/1/ -w '

'

HTTP/1.1 200 OK

...

{

"id":1,

"name":"Thailand",

"capital":"Bangkok",

"area":513120

}

该响应包含第一个Country的信息。这些示例仅涵盖了GET和POST请求。请随时尝试PUT、PATCH和DELETE请求,看看如何从REST API全面管理你的模型。

正如你所见,Django REST框架是构建REST API的绝佳选择,特别是如果你有一个现有的Django项目并希望添加一个API。

FastAPI

FastAPI是一个优化用于构建API的Python Web框架。它使用Python类型提示,并内置支持异步操作。FastAPI建立在Starlette和Pydantic之上,性能非常出色。

以下是使用 FastAPI 构建的 REST API 的示例:

# app.py

from fastapi import FastAPI

from pydantic import BaseModel, Field

app = FastAPI()

def _find_next_id():

return max(country.country_id for country in countries) + 1

class Country(BaseModel):

country_id: int = Field(default_factory=_find_next_id, alias="id")

name: str

capital: str

area: int

countries = [

Country(id=1, name="Thailand", capital="Bangkok", area=513120),

Country(id=2, name="Australia", capital="Canberra", area=7617930),

Country(id=3, name="Egypt", capital="Cairo", area=1010408),

]

@app.get("/countries")

async def get_countries():

return countries

@app.post("/countries", status_code=201)

async def add_country(country: Country):

countries.append(country)

return country

这个应用程序使用了FastAPI的特性来为相同的国家数据构建一个REST API,就像你在其他示例中看到的那样。

要尝试此应用程序,请使用pip安装fastapi:

$ python -m pip install fastapi

你还需要安装uvicorn[standard],一个可以运行FastAPI应用程序的服务器:

$ python -m pip install uvicorn[standard]

如果你已经安装了fastapi和uvicorn,那么将上面的代码保存在一个名为app.py的文件中。运行以下命令以启动开发服务器:

$ uvicorn app:app --reload

INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)

服务器现在正在运行。打开浏览器并转到http://127.0.0.1:8000/countries。你会看到FastAPI响应如下:

[

{

"id": 1,

"name":"Thailand",

"capital":"Bangkok",

"area":513120

},

{

"id": 2,

"name":"Australia",

"capital":"Canberra",

"area":7617930

},

{

"id": 3,

"name":"Egypt",

"capital":"Cairo",

"area":1010408

}

]

FastAPI以JSON数组的形式返回一个国家列表。你还可以通过向/countries发送POST请求来添加一个新国家:

$ curl -i http://127.0.0.1:8000/countries

-X POST

-H 'Content-Type: application/json'

-d '{"name":"Germany", "capital": "Berlin", "area": 357022}'

-w '\n'

HTTP/1.1 201 Created

content-type: application/json

...

{"id":4,"name":"Germany","capital":"Berlin","area": 357022}

你添加了一个新国家。你可以用GET /countries确认这一点:

$ curl -i http://127.0.0.1:8000/countries -w '\n'

HTTP/1.1 200 OK

content-type: application/json

...

[

{

"id":1,

"name":"Thailand",

"capital":"Bangkok",

"area":513120,

},

{

"id":2,

"name":"Australia",

"capital":"Canberra",

"area":7617930

},

{

"id":3,

"name":"Egypt",

"capital":"Cairo",

"area":1010408

},

{

"id":4,

"name": "Germany",

"capital": "Berlin",

"area": 357022

}

]

FastAPI返回一个包含你刚刚添加的新国家的JSON列表。

你会注意到FastAPI应用程序与Flask应用程序看起来相似。和Flask一样,FastAPI有一个专注的功能集。它并不试图处理Web应用程序开发的所有方面。它是用现代Python特性构建API的。

如果你查看app.py文件的顶部附近,你会看到一个名为Country的类,它扩展了BaseModel。Country类描述了REST API中数据的结构:

class Country(BaseModel):

country_id: int = Field(default_factory=_find_next_id, alias="id")

name: str

capital: str

area: int

这是一个Pydantic模型的示例。Pydantic模型在FastAPI中提供了一些有用的功能。它们使用Python类型注释来强制执行类中每个字段的数据类型。这使得FastAPI能够自动为API端点生成具有正确数据类型的JSON。它还允许FastAPI验证传入的JSON。

突出显示第一行是很有帮助的,因为那里发生了很多事情:

country_id: int = Field(default_factory=_find_next_id, alias="id")

在这一行中,你看到了country_id,它存储了一个整数作为Country的ID。它使用了Pydantic的Field函数来修改country_id的行为。在这个例子中,你传递给Field的关键字参数是default_factory和alias。

第一个参数default_factory设置为_find_next_id()。这个参数指定了每当创建一个新的Country时运行的函数。返回值将分配给country_id。

第二个参数alias设置为id。这告诉FastAPI在JSON中输出”id”而不是”country_id”:

{

"id":1,

"name":"Thailand",

"capital":"Bangkok",

"area":513120,

}

这个别名还意味着你在创建新的Country时可以使用id。你可以在国家列表中看到这一点:

countries = [

Country(id=1, name="Thailand", capital="Bangkok", area=513120),

Country(id=2, name="Australia", capital="Canberra", area=7617930),

Country(id=3, name="Egypt", capital="Cairo", area=1010408),

]

这个列表包含了API中初始国家的三个Country实例。Pydantic模型提供了一些很棒的功能,并允许FastAPI轻松处理JSON数据。

现在看看这个应用程序中的两个API函数。第一个,get_countries(),返回/countries GET请求的国家列表:

@app.get("/countries")

async def get_countries():

return countries

FastAPI 将根据 Pydantic 模型中的字段自动创建 JSON,并根据Python类型提示设置正确的JSON数据类型。

当你向/countries发出POST请求时,Pydantic模型还提供了一个好处。你可以看到下面的第二个API函数中,参数country有一个Country注释:

@app.post("/countries", status_code=201)

async def add_country(country: Country):

countries.append(country)

return country

这种类型注释告诉FastAPI将传入的JSON与Country进行验证。如果不符合,则FastAPI将返回一个错误。你可以通过发送一个与Pydantic模型不匹配的JSON来尝试这一点:

$ curl -i http://127.0.0.1:8000/countries \

-X POST \

-H 'Content-Type: application/json' \

-d '{"name":"Germany", "capital": "Berlin"}' \

-w '\n'

HTTP/1.1 422 Unprocessable Entity

content-type: application/json

...

{

"detail": [

{

"loc":["body","area"],

"msg":"field required",

"type":"value_error.missing"

}

]

}

由于请求中的JSON缺少area的值,所以FastAPI返回了状态码422 Unprocessable Entity以及关于错误的详细信息。这种验证是由Pydantic模型实现的。

这只是FastAPI能做什么的一个简单介绍)。凭借其高性能和像异步函数、自动文档等现代特性,FastAPI值得考虑用于你的下一个REST API。

结论

REST API 无处不在。了解如何利用 Python 来使用和构建 API ,可以让你处理网络服务提供的大量数据。

在本教程中,您学习了如何:

  • 确定 REST 架构风格
  • 使用 HTTP 方法和状态代码
  • 使用请求从外部 API 获取和使用数据
  • 定义 REST API 的终端节点数据和响应
  • 开始使用 Python 工具构建 REST API

使用新的 Python REST API 技能,您不仅可以与 Web 服务交互,还可以为您的应用程序构建 REST API。这些工具为各种有趣的、数据驱动的应用程序和服务打开了大门。

原文链接:https://realpython.com/api-integration-in-python/

#你可能也喜欢这些API文章!