识别资源
构建 REST API 时,要采取的第一步是确定 API 将管理的资源。通常将这些资源描述为复数名词,如customers 、events 、 或transactions 。在 Web 服务中标识不同的资源时,您将构建一个名词列表,用于描述用户可以在 API 中管理的不同数据。
执行此操作时,请确保考虑任何嵌套资源。例如,customers 可能具有guests 或sales 可能包含events 。在定义 API 接口时,建立这些资源层次结构将很有帮助。
定义您的接口
确定 Web 服务中的资源后,需要使用这些资源来定义 API 接口。以下是您可能在支付处理服务的 API 中找到的资源的一些示例接口:transactions
HTTP 方法 | 接口终端节点 | 描述 |
---|
GET | /transactions | 获取交易列表。 | GET | /transactions/<transaction_id> | 获取单个事务。 | POST | /transactions | 创建新事务。 | PUT | /transactions/<transaction_id> | 更新交易。 | PATCH | /transactions/<transaction_id> | 部分更新事务。 | DELETE | /transactions/<transaction_id> | 删除交易记录。 |
这六个节点涵盖了在 Web 服务中transactions 创建、读取、更新和删除所需的所有操作。Web 服务中的每个资源都将具有类似的节点列表,具体取决于用户可以使用 API 执行的操作。
注意: 节点不应包含谓词。相反,您应该选择适当的 HTTP 方法来传达终结点的操作。例如,下面的终结点包含一个不需要的谓词:
GET /getTransactions
此处,get 在不需要包含在接口中。HTTP 方法已通过指示操作为终结点提供语义含义。您可以从接口中删除:GET
GET /transactions
此接口仅包含一个复数名词,HTTP GET 方法传达操作。
现在看一下嵌套资源的接口示例。在这里,你将看到guests 嵌套在资源events 下的接口:
HTTP 方法 | 接口终端节点 | 描述 |
---|
GET | /events/<event_id>/guests | 获取来宾列表。 | GET | /events/<event_id>/guests/<guest_id> | 获取单个来宾。 | POST | /events/<event_id>/guests | 创建新的来宾。 | PUT | /events/<event_id>/guests/<guest_id> | 更新来宾。 | PATCH | /events/<event_id>/guests/<guest_id> | 部分更新来宾。 | DELETE | /events/<event_id>/guests/<guest_id> | 删除来宾。 |
使用这些接口,可以管理系统中的特定事件。guests
这不是为嵌套资源定义接口的唯一方法。有些人更喜欢使用查询字符串来访问嵌套资源。查询字符串允许您随 HTTP 请求一起发送其他参数。在以下接口中,追加查询字符串guests 以获取特定event_id :
GET /guests?event_id=23
此节点guests 将筛选出任何未引用给定event_id .与 API 设计中的许多事情一样,您需要确定哪种方法最适合您的 Web 服务。
注意: REST API 不太可能在 Web 服务的整个生命周期中保持不变。资源将发生更改,您需要更新接口节点以反映这些更改。这就是 API 版本控制的用武之地。API 版本控制允许您修改 API,而不必担心破坏现有集成。
有各种各样的版本控制策略。选择正确的选项取决于 API 的要求。以下是一些最受欢迎的 API 版本控制选项:
无论您选择哪种策略,对 API 进行版本控制都是确保其能够适应不断变化的需求同时支持现有用户的重要步骤。
选择数据交换格式
用于格式化 Web 服务数据的两个常用选项是 XML 和 JSON。传统上,XML在[SOAP] API中非常流行,但JSON在REST API中更受欢迎。若要比较两者,请看一个格式化为 XML 和 JSON 的示例资源book 。
下面是一本格式化为 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中的相同内容:book
{
"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 状态代码。
在本节中,您将查看管理cars 清单 的假设 API 的一些示例 HTTP 响应。这些示例将让您了解应如何设置 API 响应的格式。为了清楚起见,您将查看原始HTTP请求和响应,而不是使用像.requests
要开始操作,请查看GET 对 /cars 的请求,其中返回以下列表:
GET /cars HTTP/1.1
Host: api.example.com
此 HTTP 请求由四部分组成:
GET 是 HTTP 方法类型。/cars 是 API 接口。HTTP/1.1 是 HTTP 版本。主机:api.example.com 是 API 主机。
GET
这四个部分是您向/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 之间有所不同,因此在这些示例中已排除它们。
请务必始终在响应上设置正确的标头Content-Type 。如果发送 JSON,Content-Type 则设置为application/json 。如果为 XML,则将其设置为 application/xml 。此标头告诉用户应如何分析数据。
您还希望在响应中包含适当的状态代码。对于任何成功的请求,GET 应返回200 OK 。这告诉用户其请求已按预期处理。
看看另一个GET 请求,这次是针对一辆车:
GET /cars/1 HTTP/1.1
Host: api.example.com
此 HTTP 请求查询汽车1 的 API。以下是响应:
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
注意: 请求永远不应修改现有资源。如果请求包含数据,则应忽略此数据,并且 API 应返回未更改的资源。GET
POST
接下来,查看添加新车的请求: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。它将标头Content-Type 设置为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"
}
此响应具有状态代码200 OK ,201 Created 用于告知用户已创建新资源。201 Created 确保对所有成功的请求使用 而不是POST 。
此响应还包括新车的副本id ,其中包含由 API 生成的id 请务必在响应中发回 a,以便用户可以再次修改资源。
注意: 当用户使用 POST 或PUT 或PATCH 修改资源时,始终发回资源的副本非常重要。这样,用户就可以看到他们所做的更改。
PUT
现在看一下请求: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"
}
响应包括具有新数据的car 的副本。同样,您始终希望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"
}
响应包含 car 的完整副本。如您所见,只有vin 和color 字段已更新。
DELETE
最后,看看 REST API 在收到DELETE 请求时应如何响应。下面是删除 :
HTTP/1.1 204 No Content
此响应仅包括状态代码 。此状态代码204 No Content 告诉用户操作成功,但响应中未返回任何内容。这是有道理的,因为car 已被删除。没有理由在响应中将其副本发送回去。
设计错误响应
对 REST API 的请求总是有可能失败。最好定义错误响应的外观。这些响应应包括所发生错误的说明以及相应的状态代码。在本节中,您将看几个示例。
首先,请查看对 API 中不存在的资源的请求:
GET /motorcycles HTTP/1.1
Host: api.example.com
此处,用户向/motorcycles 发送不存在的请求。API 会发回以下响应:
HTTP/1.1 404 Not Found
Content-Type: application/json
...
{
"error": "The requested resource was not found."
}
此响应包括状态代码。除此之外,响应还包含一个带有描述性错误消息的 JSON 对象。提供描述性错误消息可为用户提供有关错误的更多上下文。
现在看一下用户发送无效请求时的错误响应:
POST /cars HTTP/1.1
Host: api.example.com
Content-Type: application/json
{
"make": "Nissan",
"year": 1994,
"color": "Violet"
此POST 请求包含 JSON,但格式不正确。它缺少末尾的右大括号 (} )。API 将无法处理此数据。错误响应会告知用户以下问题:
HTTP/1.1 400 Bad Request
Content-Type: application/json
{
"error": "This request was not properly formatted. Please send again."
}
此响应包括一条描述性错误消息以及状态代码,告知用户他们需要修复请求。
还有其他几种方式,即使请求的格式正确,也可能是错误的。在下一个示例中,用户发送请求,但包含不受支持的媒体类型: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."
}
此响应包括状态代码,以指示请求包含 API 不支持的数据格式。此错误代码对于格式错误的数据有意义,但是即使格式正确也无效的数据呢?
在下一个示例中,用户发送请求,但包含的数据与其他数据的字段不匹配:
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."
}
此响应包括状态代码。此状态代码表示请求没有任何问题,但数据无效。REST API 需要验证传入的数据。如果用户随请求一起发送数据,则 API 应验证数据并通知用户任何错误。
响应请求(无论是成功的还是错误的)是 REST API 最重要的工作之一。如果您的 API 直观且提供准确的响应,则用户将更容易围绕您的 Web 服务构建应用程序。幸运的是,一些优秀的Python Web框架抽象出了处理HTTP请求和返回响应的复杂性。
|