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

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   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
近日只是为了想尽办法为 Flask 实现 Swagger UI 文档功能,基本上要让 Flask 配合 Flasgger, 所以写了篇 Flask 应用集成 Swagger UI 。然而不断的 Google 过程中偶然间发现了...【详细内容】
2021-12-23  Tags: API  点击:(6)  评论:(0)  加入收藏
一个项目的大部分API,测试用例在参数和参数值等信息会有很多相似的地方。我们可以复制API,复制用例来快速生成,然后做细微调整既可以满足我们的测试需求1.复制API:在菜单发布单...【详细内容】
2021-12-14  Tags: API  点击:(20)  评论:(0)  加入收藏
10月18号, W3C中网络平台孵化器小组(Web Platform Incubator Community Group)公布了HTML Sanitizer API的规范草案。这份草案用来解决浏览器如何解决XSS攻击问题。 网络安全中...【详细内容】
2021-12-07  Tags: API  点击:(20)  评论:(0)  加入收藏
当我们通过kubectl来查看、修改Kubernetes资源时,有没有想过后面的接口到底是怎样的?有没有办法探查这些交互数据呢?Kuberenetes客户端和服务端交互的接口,是基于http协议的。所...【详细内容】
2021-11-23  Tags: API  点击:(29)  评论:(0)  加入收藏
前言客户端请求API,通常需要通过返回码来判断API返回的结果是否符合预期,以及该如何处理返回的内容等。相信很多同学都吃过返回码定义混乱的亏,有的API用返回码是int类型,有的是...【详细内容】
2021-10-28  Tags: API  点击:(52)  评论:(0)  加入收藏
凭借着平缓的学习曲线和简单直接的语法,Python在全球范围内的受欢迎程度,正在呈指数级增长。该编码语言往往可以被用于Web开发、软件开发、数学计算、系统脚本、以及几乎所有...【详细内容】
2021-09-22  Tags: API  点击:(48)  评论:(0)  加入收藏
Guava提供的RateLimiter可以限制物理或逻辑资源的被访问速率,咋一听有点像java并发包下的Samephore,但是又不相同,RateLimiter控制的是速率,Samephore控制的是并发量。RateLimit...【详细内容】
2021-09-17  Tags: API  点击:(72)  评论:(0)  加入收藏
前言前后端分离开发模式中,api文档是最好的沟通方式。今天就来说一说如何整合Swagger生成一套漂亮、美观、实用的接口文档。 源码传送门: https://gitee.com/huoqstudy/xiliu-...【详细内容】
2021-09-08  Tags: API  点击:(65)  评论:(0)  加入收藏
注:商业级功能效果演示,非开源,无源码。研发基础在之前AJAX请求数据加密效果之上,更进一步,对返回数据加密。之前是单纯用于登录场景。更广泛的场景是所有此类AJAX WEB API接口。...【详细内容】
2021-09-03  Tags: API  点击:(75)  评论:(0)  加入收藏
最近一连串的 API 安全事件(Peloton、Experian、Clubhouse 等)无疑迫使许多安全和开发团队仔细检查他们的 API 安全状况,以确保它们不会成为下一个被攻击对象。创建面向外部受...【详细内容】
2021-09-01  Tags: API  点击:(59)  评论:(0)  加入收藏
▌简易百科推荐
本文分为三个等级自顶向下地分析了glibc中内存分配与回收的过程。本文不过度关注细节,因此只是分别从arena层次、bin层次、chunk层次进行图解,而不涉及有关指针的具体操作。前...【详细内容】
2021-12-28  linux技术栈    Tags:glibc   点击:(3)  评论:(0)  加入收藏
摘 要 (OF作品展示)OF之前介绍了用python实现数据可视化、数据分析及一些小项目,但基本都是后端的知识。想要做一个好看的可视化大屏,我们还要学一些前端的知识(vue),网上有很多比...【详细内容】
2021-12-27  项目与数据管理    Tags:Vue   点击:(2)  评论:(0)  加入收藏
程序是如何被执行的  程序是如何被执行的?许多开发者可能也没法回答这个问题,大多数人更注重的是如何编写程序,却不会太注意编写好的程序是如何被运行,这并不是一个好...【详细内容】
2021-12-23  IT学习日记    Tags:程序   点击:(9)  评论:(0)  加入收藏
阅读收获✔️1. 了解单点登录实现原理✔️2. 掌握快速使用xxl-sso接入单点登录功能一、早期的多系统登录解决方案 单系统登录解决方案的核心是cookie,cookie携带会话id在浏览器...【详细内容】
2021-12-23  程序yuan    Tags:单点登录(   点击:(8)  评论:(0)  加入收藏
下载Eclipse RCP IDE如果你电脑上还没有安装Eclipse,那么请到这里下载对应版本的软件进行安装。具体的安装步骤就不在这赘述了。创建第一个标准Eclipse RCP应用(总共分为六步)1...【详细内容】
2021-12-22  阿福ChrisYuan    Tags:RCP应用   点击:(7)  评论:(0)  加入收藏
今天想简单聊一聊 Token 的 Value Capture,就是币的价值问题。首先说明啊,这个话题包含的内容非常之光,Token 的经济学设计也可以包含诸多问题,所以几乎不可能把这个问题说的清...【详细内容】
2021-12-21  唐少华TSH    Tags:Token   点击:(10)  评论:(0)  加入收藏
实现效果:假如有10条数据,分组展示,默认在当前页面展示4个,点击换一批,从第5个开始继续展示,到最后一组,再重新返回到第一组 data() { return { qList: [], //处理后...【详细内容】
2021-12-17  Mason程    Tags:VUE   点击:(14)  评论:(0)  加入收藏
什么是性能调优?(what) 为什么需要性能调优?(why) 什么时候需要性能调优?(when) 什么地方需要性能调优?(where) 什么时候来进行性能调优?(who) 怎么样进行性能调优?(How) 硬件配...【详细内容】
2021-12-16  软件测试小p    Tags:性能调优   点击:(20)  评论:(0)  加入收藏
Tasker 是一款适用于 Android 设备的高级自动化应用,它可以通过脚本让重复性的操作自动运行,提高效率。 不知道从哪里听说的抖音 app 会导致 OLED 屏幕烧屏。于是就现学现卖,自...【详细内容】
2021-12-15  ITBang    Tags:抖音防烧屏   点击:(25)  评论:(0)  加入收藏
11 月 23 日,Rust Moderation Team(审核团队)在 GitHub 上发布了辞职公告,即刻生效。根据公告,审核团队集体辞职是为了抗议 Rust 核心团队(Core team)在执行社区行为准则和标准上...【详细内容】
2021-12-15  InfoQ    Tags:Rust   点击:(25)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条