AI视频剪辑工具:解锁创作的无限可能
RESTful Web API 设计中要避免的 6 个常见错误
假设在网上订购了一张“准备组装”的桌子,却发现送货包裹中缺少了组装说明。虽然最终知道桌子的样子,但却几乎没有任何线索来指导如何开始组装各个部分。设计不佳的 API 常常给开发人员带来类似的困扰。一个设计良好的 API 能让开发人员轻松地找到、浏览、访问并使用它。在某些情况下,高质量的 API 甚至能激发新的创意,并为开发人员带来新的应用场景。
改进 API 设计有多种方法,例如遵循 RESTful 规范。但不知不觉中,开发人员往往会在 API 中埋下小小的不便。为了帮助避免这些常见问题,下面列出了开发人员在设计 API 时常犯的六个错误,并提供了改进这些错误的指导。
由内而外思考与由外而内思考
为每个人提供一切通常意味着所做的一切都要尽可能完美,API 设计也不例外。当客户使用 API 时,他们希望得到特定的解决方案,帮助他们的工作变得更加轻松和高效。如果有更适合需求的 API,他们通常会选择那个,而不是使用当前的 API。这也正是为什么理解客户需求,并根据这些需求来设计 API 是如此重要。换句话说,设计时应从客户的需求出发,进行由外而内的思考,而非从内部系统出发进行由内而外的设计。
由内而外指的是围绕您想要公开的内部系统或服务来设计 API;
由外而内则是围绕客户体验来设计 API,重点是满足客户的实际需求。在 API 产品思维中,Outside-in(由外而内)视角至关重要。
首先,向客户(无论是内部开发人员还是外部客户)学习,了解他们的使用场景是设计的第一步。与他们交流,询问他们正在构建的应用、遇到的痛点,以及哪些功能能简化他们的开发过程。记录下最关键的使用案例,并为每个使用案例创建一个示例 API 响应,确保每个场景只提供所需的最精准数据。在测试过程中,关注不同场景间的重叠部分,优化设计,使其在常见或相似的使用场景下能够复用。
如果无法直接与客户沟通——可能是因为缺乏接触、客户时间有限,或者他们并不清楚自己具体需要什么——那么最好的办法是站在使用者的角度,想象自己如何使用这个 API,进行大胆而富有创意的思考。尽管不应为了尚未实现的功能设计 API,但从宏观角度出发的设计会更有利于将来进行无缝的调整和改进。例如,下图展示了 Google Maps 提供的 API,即使没有深入文档,仅通过“Autocomplete”或“Address Validation”这些名称,也可以很清楚地推测其用途和潜在的客户应用场景。
避免使 API 过于复杂
客户使用 API 的目的是希望绕过复杂的编程挑战,以便能够专注于他们擅长的部分。如果使用您的 API 需要学习全新的系统或语言,客户可能会觉得这不符合他们的需求,从而寻求其他更简便的解决方案。因此,团队设计的 API 应既强大又智能,能够满足客户需求,同时也要足够简洁,能够隐藏背后复杂的实现细节。
例如,如果您知道客户通过 API 向消费者提供有关最近开业的餐厅和评价较高的比萨店的信息,那么一个简单明了的 API 调用将会非常有帮助,例如:
GET /restaurants?location=Austin&category=Pizzeria&open=true&sort=-priority,created_at
要检查您的 API 是否足够简洁,可以假装从零开始构建整个系统,或者请愿意提供反馈的可信赖客户进行测试并报告结果。如果您能够顺利完成整个工作流程,而无需在过程中停下来思考或解决问题,那就说明设计已相当成熟。相反,如果在编码过程中发现自己不断因系统的复杂性而感到困惑,说明还需要进一步优化。当能够确保 API 不会让用户感到困惑,并且既能满足当前需求,又能灵活应对需求变化时,您的 API 就可以准备发布了。
避免创建过多的“聊天”API
频繁的网络调用会显著降低系统性能,并增加连接开销,从而导致更高的运营成本。因此,减少 API 调用次数是优化 API 设计的一个关键因素。
关键在于由外而内的设计思维:简化。需要寻找方法,减少客户在其应用程序中必须执行的 API 调用次数。例如,如果客户正在开发移动应用程序,他们通常需要最大限度地减少网络流量,以降低电池消耗。在这种情况下,减少从多个调用到单个调用的转变,可能对性能和用户体验产生显著影响。
在设计时,不必在构建独特的数据驱动微服务和简化 API 使用之间做出选择,理想的做法是同时提供两者:针对特定数据类型的精细 API 和专注于提升用户体验的“体验 API”(Experience API)。这些体验 API 将多个小型功能整合到一个端点中,从而帮助客户(尤其是构建用户界面的开发者)更轻松、快速地展示数据。例如,对于常见的用户界面,您可以提供一个集成了多个数据源的 API,让客户能够直接呈现信息,而无需多次调用。
另一种选择是使用像 GraphQL 这样的技术,来实现 API 的定制化。虽然通常不建议为每个可能的屏幕构建独立的终端节点,但像主页、用户账户信息等常见屏幕的 API 设计,如果能够合并为一个简洁的调用,将大大提高效率,并显著改善 API 用户的体验。
避免限制 API 灵活性
即使遵循了所有设计原则,仍然可能会遇到一些边缘情况,这些情况不适合当前设计的标准有效负载。可能是某些客户在单个结果页面上需要比平常更多的数据,或者返回的数据量远超应用程序的实际需求。在这种情况下,虽然不可能为所有情况提供统一的解决方案,但也不应让 API 变得过于限制性。为了让您的 API 更加灵活,以下是三种简单的改进方法:
1. 筛选响应属性
可以通过查询参数来控制返回的数据类型,例如排序和分页,或者使用像 GraphQL 这样的技术来实现更加灵活的查询。让客户选择只请求他们需要的属性,避免返回过多不必要的数据。例如,如果某些客户只需要书名、作者和畅销书排名,可以通过查询参数仅获取这些数据:
GET /books?fields=title,author,ranking
2. 支持分页和排序
通常,您可能不希望 API 保证响应中对象的顺序,因为数据源的微小变化可能会导致排序顺序发生变化。然而,在某些情况下,客户可能需要按特定字段对数据进行排序。提供分页选项,并允许用户根据需求对数据进行排序,这样可以提高 API 的效率。例如,Spotify API 就是通过简单的 offset
和 limit
参数来实现分页,帮助用户高效获取数据。以下是一个示例:
$ curl https://api.spotify.com/v1/artists/1vCWHaC5f2uS3yhpwWbIA6/albums?album_type=SINGLE&offset=20&limit=10
3. 使用 GraphQL 等技术实现动态查询
由于客户的需求各异,提供动态数据组合的能力可以让他们根据具体需求构建查询,而不必局限于固定的数据类型或预定义的字段组合。GraphQL 提供了这种灵活性,使客户能够按需请求数据,避免了构建多个不同终端节点的麻烦。如果不能使用 GraphQL,也可以通过查询字符串参数(如 expand
)来支持更复杂的查询。以下是一个示例响应,展示了包含嵌套属性的公司资源集合:
"data": [
{
"CompanyUid": "27e9cf71-fca4",
"name": "ABCCo",
"status": "Active",
"_embedded": {
"organization": {
"CompanyUid": "27e9cf71-fca4",
"name": "ABCCo",
"type": "Company",
"taxId": "0123",
"city": "Portland",
"notes": ""
}
}
}
]
通过这些改进,您可以在满足不同需求的同时,保持 API 的灵活性和高效性。
避免让设计对人类来说不可读
“KISS”原理(Keep It Simple, Stupid)在 API 设计中尤为重要。虽然 API 是为了计算机间的交互而设计的,但 API 的首要客户端始终是人类,而 API 协定本身就是文档的一部分。开发人员通常在深入查看文档之前,会首先关注 API 的有效负载设计。研究表明,开发人员大约 51% 的时间是在编辑器和客户端中度过的,约 18% 的时间是在查阅文档。
例如,下面的有效负载设计需要一些时间来理解,因为其中使用了“id”这一通用字段名,而没有清晰地描述属性的意义。即使是“data”这一属性名,除了 JSON 格式的约定外,并没有明确表达其具体含义。多加一些字节,选择更具描述性的字段名,可以有效避免混淆并加速 API 的采用。以下是一个例子,注意 “user-ids” 在 JSON 结构中的出现,如何让开发人员在读取时产生疑惑:
"{id-a}":
{
"data":
[
{
"AirportCode": "LAX",
"AirportName": "Los Angeles",
"From": "LAX",
"To": "Austin",
"departure": "2014-07-15T15:11:25+0000",
"arrival": "2014-07-15T16:31:25+0000"
}
]
}
这样的 JSON 设计可能让开发人员在理解数据时感到困惑。因此,保持有效负载的简单和直观是很重要的。如果某些字段名称可以有多种解释,最好做出调整,确保它们一目了然。以下是来自 aviationstack API 的 Airlines 端点响应示例,展示了如何通过清晰、简洁的字段名,使得数据易于理解:
"data": [
{
"airline_name": "American Airlines",
"iata_code": "AA",
"iata_prefix_accounting": "1",
"icao_code": "AAL",
"callsign": "AMERICAN",
"type": "scheduled",
"status": "active",
"fleet_size": "963",
"fleet_average_age": "10.9",
"date_founded": "1934",
"hub_code": "DFW",
"country_name": "United States",
"country_iso2": "US"
},
[...]
]
可以看到,字段名称直接反映了数据的含义,保持了 JSON 结构的简单性,同时明确地传达了预期结果。这种设计能大大减少开发人员在理解数据时的困惑,提高 API 的易用性。
知道何时可以打破 RESTful 规则
遵循 RESTful 基础规范(例如正确使用 HTTP 动词、状态码和基于资源的无状态接口)能够让 API 更加直观,帮助开发者避免学习全新的词汇和规则。然而,值得注意的是,API 的最终目标是帮助用户高效地完成任务。如果过于严格地遵循 RESTful 设计规范,可能会影响用户体验,导致 API 的使用变得更加复杂,进而偏离其初衷。
在设计 API 时,目标应始终是帮助客户尽可能快速、轻松地使用数据并取得成功。有时,为了提供更加简洁和优雅的接口,可能需要打破一些 RESTful 规则。关键是要确保在 API 设计中保持一致性,并在文档中清楚标明任何可能偏离标准的设计决策。
例如,在某些情况下,为了优化性能或简化开发过程,您可能会选择合并多个资源操作到单一端点,或者在特定场景下使用自定义的 HTTP 方法和状态码。尽管这些设计可能违背传统的 RESTful 规则,但如果能显著提升 API 的易用性并帮助开发人员更快实现目标,那么这些调整就是合理的。
总之,虽然遵循 RESTful 规范是一个良好的起点,但最重要的是始终以客户的需求为中心,在设计时适时灵活调整,确保 API 的实用性和高效性。
结论
除了上述常见的设计陷阱外,我们还整理了一份全面的指南,结合了我们在 API 设计与管理方面的丰富经验,并将其与 Google Cloud 的 API 管理平台 Apigee 相结合。
Apigee 是 Google Cloud 的原生 API 管理平台,能够帮助您构建、管理和保护 API,适用于各种用例、规模和环境。立即开始使用 Apigee,或查看我们的文档以获取更多信息。