您当前的位置:首页 > 电脑百科 > 程序开发 > 编程百科

API设计的七宗罪

时间:2020-01-30 09:14:34  来源:  作者:

API设计的七宗罪

Learning from bad examples

我当时正在帮助一个需要将物业管理系统中的房屋可用性与客户网站集成的朋友。 幸运的是,物业管理系统具有API。 而不幸的是,这一切都错了。

这个故事的目的不是给二手系统带来不好的广告,而是分享不应该开发的东西,以及在设计API时学习正确的方法。

任务

我朋友的客户正在使用Beds24系统管理他们的财产清单,并在各种预订系统(预订,AirBnB等)之间保持可用性同步。 他们正在建立一个网站,并希望搜索机制仅显示可用于所选日期和客人人数的属性。 听起来像是一项艰巨的任务,因为Beds24提供了与其他系统集成的API。 不幸的是,开发人员在设计时已经犯了很多错误。 让我们逐步解决这些错误,看看到底出了什么问题以及应该如何解决。

一宗罪:请求正文格式

由于只对获取客户端属性的可用性感兴趣,因此仅对/ getAvailabilities调用感兴趣。 即使这是获取可用性的调用,但实际上这是一个POST请求,因为作者决定接受过滤器作为JSON正文。 以下是所有可能参数的列表:

{
    "checkIn": "20151001",
    "lastNight": "20151002",
    "checkOut": "20151003",
    "roomId": "12345",
    "propId": "1234",
    "ownerId": "123",
    "numAdult": "2",
    "numChild": "0",
    "offerId": "1",
    "voucherCode": "",
    "referer": "",
    "agent": "",
    "ignoreAvail": false,
    "propIds": [
        1235,
        1236
    ],
    "roomIds": [
        12347,
        12348,
        12349
    ]
}

 

让我遍历JSON对象并解释其参数出了什么问题。

· 日期checkIn,lastNight和checkOut使用YYYYMMDD格式设置。 将日期编码为字符串时,绝对没有理由不使用标准ISO 8601格式(YYYY-MM-DD),因为这是大多数开发人员和JSON解析器都理解并期望的广泛采用的标准。 除此之外,lastNight字段似乎是多余的,因为提供了checkOut,它总是比前一天晚一天。 请始终使用标准日期编码格式,不要要求API用户提供冗余数据。

· 所有Id以及numAdult和numChild字段都是数字,但被编码为字符串。 在这种特殊情况下,似乎没有理由将它们编码为字符串。

· 我们具有以下字段对:roomId和roomIds以及propId和propIds。 因为可以使用roomIds和propIds传递ID,所以具有roomId和propId属性不仅是多余的,而且这里还存在类型问题。 请注意,roomId需要一个字符串,而roomIds需要一个数字数组。 这可能会造成混乱,解析问题,并且意味着即使我们在谈论相同的数据,后端本身也会对字符串执行某些操作,并对数字执行某些操作。

请不要将开发人员犯此类愚蠢的错误,并尝试使用标准格式,并注意冗余和字段类型。 不要只是将所有内容包装在字符串中。

二宗罪:响应正文格式

如上一部分有关请求正文格式的说明,我们仅关注/ getAvailabilities调用。 这次让我们看一下响应主体格式,看看有什么问题。 请记住,我们有兴趣获取给定日期和许多客人可用的属性的ID。 以下是相应的请求和响应正文:

请求:

{
    "checkIn": "20190501",
    "checkOut": "20190503",
    "ownerId": "25748",
    "numAdult": "2",
    "numChild": "0"
}

响应:

{
    "10328": {
        "roomId": "10328",
        "propId": "4478",
        "roomsavail": "0"
    },
    "13219": {
        "roomId": "13219",
        "propId": "5729",
        "roomsavail": "0"
    },
    "14900": {
        "roomId": "14900",
        "propId": "6779",
        "roomsavail": 1
    },
    "checkIn": "20190501",
    "lastNight": "20190502",
    "checkOut": "20190503",
    "ownerId": 25748,
    "numAdult": 2
}

 

· ownerId和numAdult在响应中突然变成数字,而不是在请求正文中为字符串。

· 没有属性列表。相反,属性是使用roomId作为键的顶级对象。这意味着,为了获取可用属性的列表,我们需要遍历所有对象,检查是否存在某些参数(例如roomsavail),舍弃其他参数(例如checkIn,lastNight等)以获取属性列表。然后,我们需要检查roomsavail属性的值,如果该值大于0,则将该属性视为可用。但是请稍等,在这里仔细查看:" roomsavail":" 0",在这里" roomsavail":1.看到模式了吗?如果没有可用的房间,则值为字符串,而如果有可用的房间,则为数字!这将在诸如JAVA之类的强制执行类型安全的语言中引起很多问题,因为同一属性不应属于不同的类型。请使用适当的JSON列表来显示数据集合,而不要像上面那样使用奇怪的键值构造,并确保不要在对象之间更改字段类型。正确格式的响应如下所示(请注意,这种格式也可以在不重复任何内容的情况下获取有关房间的信息):

{
    "properties": [
        {
            "id": 4478,
            "rooms": [
                {
                    "id": 12328,
                    "available": false
                }
            ]
        },
        {
            "id": 5729,
            "rooms": [
                {
                    "id": 13219,
                    "available": false
                }
            ]
        },
        {
            "id": 6779,
            "rooms": [
                {
                    "id": 14900,
                    "available": true
                }
            ]
        }
    ],
    "checkIn": "2019-05-01",
    "lastNight": "2019-05-02",
    "checkOut": "2019-05-03",
    "ownerId": 25748,
    "numAdult": 2
}

 

三宗罪:错误处理

此API中的错误处理是通过以下方式实现的:即使发生错误,所有请求也会返回200的响应代码。 这意味着除了解析正文并检查是否存在error或errorCode字段之外,没有其他方法可以区分响应是成功还是失败。 该API仅包含6个错误代码,如下所示:

API设计的七宗罪

Error codes of Beds24 API

请考虑在出现问题时不要使用这种返回响应代码200(成功)的方法,除非这是在API框架中使用的标准方法。 优良作法是使用大多数客户端和开发人员都可以识别的标准HTTP错误代码。 例如,以上屏幕快照中的错误代码1009应该替换为401(未经授权)HTTP代码。 如果API客户端可以预先知道是否解析主体以及如何解析主体(作为数据对象或错误对象),则将使工作变得更轻松。 如果错误是特定于应用程序的,则最好在响应正文中返回400(错误请求)或500(服务器错误)以及相应的错误消息。

对于给定的API选择哪种错误处理策略,只要确保它是一致的并符合广泛采用的HTTP标准即可。 这将使我们的生活更轻松。

四宗罪:"准则"

以下是文档中API使用的"准则":

使用API时请遵守以下准则

1.呼叫应设计为仅发送和接收所需的最少数据。

2.一次仅允许一个API调用,您必须等待第一个调用完成才能开始下一个API调用。

3.多个呼叫之间的间隔应间隔几秒钟。

4. API调用应尽量少用,并保持在合理的业务使用所需的最低限度内。

5. 5分钟内过度使用将导致您的帐户被封锁,而不会发出警告。

6.我们保留自行决定禁用任何我们认为过度使用API函数的访问的权利,恕不另行通知。

虽然第1点,第4点有意义,但我不同意其他观点。 让我解释一下原因。

2.如果您正在构建REST API,则应假定该API是无状态的,在任何时间点都不应存在任何状态。 这是REST在云应用程序中有用的原因之一。 如果发生故障,可以自由地重新部署无状态组件,并且它们可以根据负载变化进行扩展。 请确保在设计RESTful API时,它实际上是无状态的,并且开发人员无需关心"一次请求"之类的事情。

3.这是一个模棱两可,非常奇怪的准则。 不幸的是,我无法弄清楚作者创建此"指南"的原因,但是它给人一种感觉,即在请求本身之外进行了一些处理,因此,紧接彼此进行调用可能会使系统处于某种错误状态 。 同样,作者说"几秒钟"的事实并没有提供有关两次请求之间实际时间的具体信息。

再次参见图5和6。在这种情况下,没有解释什么是"过度"。 是每秒10个请求还是1个? 此外,某些网站将拥有大量流量,并且仅由于这些原因而阻止它们使用API,而不会发出任何警告,可能会使开发人员远离此类系统。 请在制定此类指南时具体说明,并在提出此类规则时考虑用户。

五宗罪:文件

API文档的外观如下所示:

API设计的七宗罪

Beds24 API documentation look&feel

这里唯一的问题是可读性和整体感觉。 如果作者使用markdown而不是自定义非样式html,则同一文档的外观可能会更好。 为了这篇文章的缘故,我在Dilliger的帮助下不到2分钟创建了一个更好的版本。 结果如下:

API设计的七宗罪

Styled version of the documentation

请使用工具创建API文档。 对于简单的文档,上面的一个markdown文件就足够了,而对于更大,功能更丰富的文档,最好使用Swagger或Apiary之类的工具。

这是Beds24 API文档的链接,适合那些想要自己看一下的人。

六宗罪:安全

API文档规定了关于所有端点的以下内容:

要使用这些功能,必须在菜单设置>>帐户>>帐户访问中允许API访问。

但是,实际上,任何人都可以请求获取数据,而无需为某些调用传递任何凭据,例如获取特定属性的可用性。 在文档的不同部分中对此进行了说明:

大多数JSON方法都需要API密钥才能访问帐户。 可以在菜单>>帐户>>帐户访问中设置API代码。

除了上面关于身份验证的沟通不畅之外,API密钥实际上是用户需要自己创建的东西(实际上是手动键入密钥,绝不自动生成),并且其长度应在16到64个字符之间。 允许用户自己创建密钥可能会导致非常不安全的密钥(很容易猜到)或某些格式问题,因为可以在帐户设置的该字段中输入任何字符串。 在最坏的情况下,它也可能成为SQL注入或其他类型攻击的门户。 请不要让用户创建API密钥。 始终为他们提供不可变的自动生成的密钥,并能够在需要时使其无效。

对于那些实际经过身份验证的请求,我们有一个不同的问题:身份验证令牌必须作为请求主体的一部分发送,如下所示(从文档中可以看出):

API设计的七宗罪

Beds24 API authentication example

在请求正文中包含身份验证令牌意味着服务器将需要首先解析请求正文,提取密钥,执行身份验证,然后决定如何处理请求:是否执行请求。 如果成功通过身份验证,则不会涉及任何开销,因为无论如何都将解析该正文。 万一验证失败,服务器将完成上述所有工作,只是提取令牌,从而浪费宝贵的处理时间。 相反,更好的方法是使用Bearer身份验证方案或类似方法将身份验证令牌作为请求标头发送。 这样,服务器仅在成功认证的情况下才需要解析请求主体。 使用诸如Bearer令牌之类的标准身份验证方案的另一个原因仅仅是因为大多数开发人员都熟悉它。

罪过7:性能

最后但并非最不重要的一点是,请求完成平均需要1秒钟多一点的时间。 在现代应用中,这种延迟可能是不可接受的。 因此,在设计API时要考虑性能。

 

尽管上面解释了API的所有问题,但它确实可以完成。 但是,对于开发人员来说,理解和实现它需要花费数倍的时间,并且需要花费大量时间来编写更复杂的解决方案以解决琐碎的问题。 因此,在发布API之前,请考虑让开发人员实现您的API。 确保文档完整,清晰且格式正确。 检查您的资源名称是否遵循约定,数据结构是否正确,易于理解和使用。 另外,请注意安全性和性能,不要忘记正确执行错误处理。 如果在设计API时将上述所有因素都考虑在内,那么就不需要像前面的示例中那样奇怪的"准则"。

如前所述,这篇文章的目的不是让您永远不要使用Beds24或任何类似的系统,因为它们的API没有正确实现。 目标是通过分享一个不好的例子并解释如何更好地完成软件来提高软件产品的质量。 希望这篇文章可以使某人更多地关注软件开发最佳实践,并使软件系统更好。 直到下一次!

 

(本文翻译自Robert Konarskis的文章《How NOT to design APIs》,参考:https://blog.usejournal.com/how-not-to-design-restful-apis-fb4892d9057a)



Tags:API   点击:()  评论:()
声明:本站部分内容来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除,谢谢。
▌相关评论
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表
▌相关推荐
在当前的互联网环境下,尤其是移动互联网的时代,用户通过手机APP可访问很多应用,作为应用的服务部分面对日益增多的客户,为了保证用户功能和体验,必然需要采用分布式等架构,以确保...【详细内容】
2020-11-06   API  点击:(6)  评论:(0)  加入收藏
定位拒绝CRUD。用尽可能简单的方式,完成尽可能多的需求。通过约定的方式 实现统一的标准。告别加班,拒绝重复劳动,远离搬砖概述"Rocket-API" 基于spring boot 的API敏捷开发框...【详细内容】
2020-11-06   API  点击:(5)  评论:(0)  加入收藏
Linux网络编程API函数初步剖析今天我们来分析一下前几篇博文中提到的网络编程中几个核心的API,探究一下当我们调用每个API时,内核中具体做了哪些准备和初始化工作。 1、socket...【详细内容】
2020-11-03   API  点击:(3)  评论:(0)  加入收藏
在这篇文章,我们将讨论一些各种 GraphQL 部署和迁移的安全风险,这些安全风险在客户管理过程中被发现。我们会讨论比较常见的高风险权限漏洞,以及不太常见的服务端请求伪造(SSRF)...【详细内容】
2020-10-23   API  点击:(6)  评论:(0)  加入收藏
安卓 root 手机1、准备 root 过的 android 手机,关于自己手机root方法,自行查找解决。adb rootadb disable-verity2、并重新挂载分区,使系统根目录可写入adb shell "mount -o r...【详细内容】
2020-10-20   API  点击:(5)  评论:(0)  加入收藏
简介java中多线程的开发中少不了使用Thread,我们在使用Thread中提供的API过程中,应该注意些什么规则呢?一起来看一看吧。start一个ThreadThread中有两个方法,一个是start方法,一...【详细内容】
2020-10-19   API  点击:(5)  评论:(0)  加入收藏
应用场景:在API的测试中,测试某些具体数据值,比如返回的结果是否是需求的类型,文件是否是符合且具备完整的数据结构。这些都是必须且很细致的测试工作。另外,组织、运行测试场景,...【详细内容】
2020-10-19   API  点击:(13)  评论:(0)  加入收藏
在研究云系统提供的持久性时,想确保自己了解基本知识。首先阅读NVMe规范,以了解disks提供的保证(https://www.evanjones.ca/durability-nvme.html)。简单来说,你应该假设,在发出...【详细内容】
2020-10-15   API  点击:(5)  评论:(0)  加入收藏
一 背景介绍下图是我从网络上找到的一个微服务架构的简单架构图,如图可见 API Gateway 在其中起到一个承上启下的作用,是关键组件。图片来源于网络在更通用的场景下我们会使用...【详细内容】
2020-10-14   API  点击:(7)  评论:(0)  加入收藏
本文将从云原生时代的机遇和挑战说起,介绍一个全新的开源高性能云原生 API 网关——Apache APISIX,探讨如何解决云原生时代 API 网关所面临的一些痛点,最后介绍该开...【详细内容】
2020-10-13   API  点击:(7)  评论:(0)  加入收藏
原文地址:https://fredal.xin/build-api-gateway原文作者:fredal的博客 随着这些年微服务的流行,API网关已经成为微服务架构中不可或缺的一环。一方面它承担着服务对外的唯一...【详细内容】
2020-10-13   API  点击:(11)  评论:(0)  加入收藏
一、axios的封装在vue项目中,和后台交互获取数据这块,我们通常使用的是axios库,它是基于promise的http库,可运行在浏览器端和node.js中。他有很多优秀的特性,例如拦截请求和响应...【详细内容】
2020-10-12   API  点击:(9)  评论:(0)  加入收藏
之前一直用Flask,今年看到这个FastAPI框架,感觉还不错,体验了下,很容易就入门。开始学习FastAPI特点官方描述从官方的描述来看,有以下特点: 高性能,与NodeJS和Go相当,最快的python...【详细内容】
2020-10-12   API  点击:(7)  评论:(0)  加入收藏
通过一个可视化、拖拽式的界面,LoadUI允许您实时、交互式地创建、配置和重分配负载测试。在单一测试环境下,LoadUI提供完整的测试覆盖,支持所有标准的协议和技术。它功能强大,能...【详细内容】
2020-09-29   API  点击:(8)  评论:(0)  加入收藏
数据管道(Data Pipeline)是一种允许数据通过数据分析过程从一个位置高效流向另一个位置的软件。这就好比一条传送带,它能高效、准确地将数据传送到流程的每一步。例如,数据管道...【详细内容】
2020-09-18   API  点击:(7)  评论:(0)  加入收藏
什么是API请求构建工具在移动互联网时代,面向多端开发成为主流,需要向用户提供如:安卓App、苹果App、WAP、小程序、Web网页等等多种应用入口,这些入口称为前端。而为不同前端提...【详细内容】
2020-09-16   API  点击:(131)  评论:(0)  加入收藏
在企业微信创建自建应用1、登录企业微信后台,在“应用管理>自建”中点击“创建应用”,填写应用信息创建。创建应用API配置表参数值获取1、corpid:企业ID。在企业微信后台的“...【详细内容】
2020-09-11   API  点击:(8)  评论:(0)  加入收藏
近年来,深度学习领域的进展与深度学习框架的开发同步进行。这些框架为自动微分和 GPU 加速提供了高级且高效的 API,从而可以利用相对较少和简单的代码实现极度复杂和强大的深...【详细内容】
2020-09-11   API  点击:(13)  评论:(0)  加入收藏
前言官网:Mybatis-plus官方文档 简化 MyBatis !创建数据库数据库名为mybatis_plus创建表创建user表DROP TABLE IF EXISTS user;CREATE TABLE user(id BIGINT(20) NOT NULL C...【详细内容】
2020-09-07   API  点击:(4)  评论:(0)  加入收藏
API让天下没有难做的生意,黑客也是这么认为的。在企业数字化转型如火如荼的今天,API已经远远超出了技术范畴,互联网商业创新和传统企业数字化转型都离不开API经济或者API战略。...【详细内容】
2020-09-01   API  点击:(5)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条