• 如何设计出优美的Web API?


    热文索引,坚持原创不易,请小伙伴们不吝推荐支持:

    1. 程序员必须掌握的性能调优 X Y Z

    2. 程序员必须懂的架构入门课 1 2 3

    3. 如何把单体式应用拆解成微服务?【上】

    4. 如何把单体式应用拆解成微服务?【下】

    5. 如何写好产品帮助文档?

    • 概述

    WEB API的应用场景非常丰富,例如:将已有系统的功能或数据开放给合作伙伴或生态圈;对外发布可嵌入到其他网页的微件;构建前后端分离的WEB应用;开发跨不同终端的移动应用;集成公司内部不同系统等等。在上述场景里,你可能是WEB API的使用者,也可能是设计者,但你知道如何评判WEB API的优劣吗?

    • 评判标准

    我们可以从三个维度来评判一个WEB API的优劣:

    • 易于使用:WEB API的用户是程序还是人?我觉得首先是人,然后是程序。为什么这么说呢?是否采用某个WEB API的决定是人做出的,一个好的WEB API必须符合人的审美,例如:简短易记、通俗易懂、便于输入等。从程序角度看,WEB API应该遵循行业规范,在调用时不需要做特殊化处理,有利于复用已有的代码或工具。
    • 便于更改:一个WEB API发布上线之后,免不了要根据真实用户的反馈或者业务发展的需要做更新修改,这些更新修改必须尽量不影响用户。要么提供多版本支持,要么给用户提供切实可行的更新策略等等。
    • 健壮稳定:对外公开的WEB API存在被攻击的风险,以及无法准确预估的访问量等,一个好的WEB API必须要有防注入、防篡改、防重放等安全机制,还要在访问量急剧上涨时避免服务被击穿。

    做到了上述三个方面,我们才有底气将一个WEB API对外开放,接受公众的检验。好的WEB API不仅方便使用,还助于提升个人或企业的技术影响力,从而形成正向循环,带来越来越多的业务价值。为了设计出优美的WEB API,我们需要了解与之相关的设计规范和事实标准,并且在设计开发过程中尽量遵循它们。

    • 设计规范

    URI

    • 便于输入的URI,简短不冗余。每个WEB API都是一个服务,那下面反例当中的“service”就是冗余的,而且“api”也重复出现了两次,这种冗余都不利于记忆和输入:
    反例:http://api.example.com/service/api/users
    正例:http://api.example.com/users
    • 容易读懂的URI,不要随意采用缩写,缩写必须要符合国际标准规范,不要凭空发明创造,例如:国家代码定义(ISO3166)。反例中出现了两处缩写“sv”、“u”,在没有附加说明的情况下,用户压根不知道含义:
    反例:http://api.example.com/sv/u
    • 没有大小写混用的URI。HTTP协议(RFC7230)规定:除了模式(schema)和主机名以外,URI的其他信息都要区分字母的大小写。下述两个反例大小写混用,不方便记忆。
    反例:http://api.example.com/Users/12345
    反例:http://example.com/API/getUserName
    • 易于修改的URI,命名存在可预见的规律。下述正例我们可以很容易猜测改变最后的ID就可以访问其他商品的信息。
    正例:http://api.example.com/v1/items/123456
    • 不会暴露服务端架构的URI,URI只需要体现功能、数据结构和含义,无需暴露服务端如何运作的信息。这些信息对用户来说没有意义,还存在潜在的风险,恶意用户或者黑客会利用这些信息来寻找漏洞,发起对服务的攻击。
    反例:http://api.example.com/cgi-bin/get_user.php?user=100
    • 规则统一的URI,确保采用统一的规则和风格,方便用户记忆和使用。下述反例中第一个URI采用了查询参数,第二个URI采用了路径参数,这两者没有保持一致,容易造成混乱。
    反例:获取好友信息,http://api.example.com/friends?id=100
    反例:发送消息,http://api.example.com/friend/100/messages
    正例:获取好友信息,http://api.example.com/friends/100
    正例:发送消息,http://api.example.com/friends/100/messages
    • URI最好由名词组成。URI的全称是统一资源定位符(Uniform Resource Identifier),用于标识资源在互联网上的位置,类似于邮寄地址,而地址都是由名词组成的。在名词使用上也有一些需要注意的事项:其一,使用名词复数形式;其二,尽量采用多数API中使用的表示相同含义的单词;其三,通过尽可能少的单词来表示;其四,尽可能不用奇怪的缩略语等。
    • 不使用空格及需要编码的字符,例如在URI中使用中文等。
    • 使用连接符(-)来连接多个单词,推荐脊柱法:首先,URI里的主机名(域名)允许使用连字符而禁止使用下划线,且不区分大小写。其次,点字符具有特殊含义,为了与主机名的规则保持一致。
    脊柱法:http://api.example.com/v1/users/12345/profile-image
    蛇形法:http://api.example.com/v1/users/12345/profile_image
    驼峰法:http://api.example.com/v1/users/12345/profileImage

    查询参数

    • 许多场景下需要通过API分批次获取数据,我们会经常纠结采用什么样的查询参数,业界有两种常用的参数设计(per-page与page、limit与offset),用于标识每次获取的数据量和起始位置。在分批次获取数据的过程中,数据集合中的记录可能发生增删改变,我们需要注意采用相对位置或绝对位置所带来的不同效果。
    风格1:http://api.example.com/friends?per-page=50&page=3
    风格2:http://api.example.com/friends?limit=50&offset=100
    • 在设计过滤的参数时,业界也有一些事实标准可供参考。如果我们期望查询结果的特定属性取值跟过滤参数的取值完全相同,那过滤参数的名称通常为属性名;如果我们期望查询结果任意属性部分包含过滤参数的取值,那过滤参数的名称通常为“q”。
    完全符合:http://api.example.com/v1/users?name=ken
    全文搜索:http://api.example.com/v1/users?q=ken
    • URI是否可以包含动词“search”?通常以搜索为主的在线服务API可以包含,除此之外建议采用名词复数形式。常用英文单词“search”和“find”都有查找的含义,但两者还是有一些细微的差别,其中“search”用于模糊搜索,而“find”用于精准查询。
    模糊搜索:http://yboss.yahooapis.com/ysearch/web?q=ipod
    • 某个属性究竟是作为URI路径的构成元素还是作为查询参数呢?我们可以按照以下规则来判断:如果该属性信息可以唯一定位资源,那么它就适合作为路径构成元素,否则就作为查询参数;如果该属性可以省略,那么它就是适合作为查询参数。
    路径元素:http://api.example.com/v1/users/{id}
    查询参数:http://api.example.com/v1/users?name=ken

    HTTP方法

    按照HTTP协议设计的本意,URI用于标识被操作的目标对象(资源),而HTTP方法则是表示操作方法。基于HTTP协议的简单对象访问协议SOAP逐渐被RESTful的原生HTTP协议取代,我们也没有必要画蛇添足,最好就是吃透HTTP协议,充分利用它的特性。

    GET /v1/users/123 HTTP/1.1
    Host: api.example.com
    • GET,获取资源
    • POST,新增资源
    • PUT,更新已有资源
    • DELETE,删除资源
    • PATCH,更新部分资源
    • HEAD,获取资源的元信息

    如果遇到上述HTTP方法无法覆盖的场景,那通常是资源的设计粒度太大了,我们可以把粗粒度的资源分解成多个细粒度的资源。在使用HTTP协议设计WEB API的专业能力上,业界将其划分为四个层级,LEVEL3相对较理想化,缺乏实施的基础,LEVEL2是切实可行的:

    • LEVEL 0:使用HTTP
    • LEVEL 1:引入资源的概念
    • LEVEL 2:引入HTTP动词(GET/POST/PUT/DELETE等)
    • LEVEL 3:引入HATEOAS概念

    响应数据

    常用的数据格式有:HTML、XML、JSON、YAML等,如果我们的服务在响应时支持不同类型的数据格式,那应用在调用服务时如何获得期望格式的响应数据呢?通常我们可以考虑采用下述几种指定数据格式的方法:

    • 使用查询参数的方法:
    示例:https://api.example.com/v1/users?format=xml
    • 使用扩展名的方法:
    示例:https://api.example.com/v1/users.json
    • 使用在请求首部指定媒体类型的方法,优先推荐此种方法:
    GET /v1/users
    Host: api.example.com
    Accept: application/json

    响应数据应该包含哪些信息呢?是否越多越好?亦或越少越好,仅仅包含ID?建议是按需返回,根据业务功能所需返回相应的数据。如果一个WEB API需要提供给不同业务场景使用,不同业务场景对数据属性信息的要求不同,或多或少,这种情况我们可以让用户来选择响应的内容,选择方法就是通过查询参数指定:

    示例:http://api.example.com/v1/users/123?fields=name,age
    

    响应数据的结构应该尽量扁平化,不要嵌套太深,减少没有具体含义的信息载荷,这样既可以压缩报文尺寸,又可以节省带宽的。当然,如果层级结构更具优势,也可以采用。

    出错信息

    建议通过HTTP协议首部的状态码来表示出错信息,而不是再封装一层,遵守协议规范的好处是可以减少沟通的成本,也可以利用许多成熟的软硬件产品来处理异常出错信息。HTTP协议定了了五种类型的状态码:

    • 1XX:消息
    • 2XX:成功
    • 3XX:重定向
    • 4XX:客户端原因引起的错误
    • 5XX:服务器端原因引起的错误

    我们需要每种状态码的使用场景,确保正确使用状态码。除此之外,服务还需要向客户端返回详细的出错信息,我们通常可以采用下述两种方法来传递详细的出错信息:

    • 方法1:定义私有的首部,将其填入响应消息的首部。
    • 方法2:将详细的出错信息放入消息体。

    版本管理

    随着业务的发展,每个发布上线的WEB API都存在更新修改的可能,那就需要引入版本管理的机制。业界有三种常见的标注WEB API版本的方法:

    • 在URI中嵌入版本编号:
    示例:http://api.linkedin.com/v1/people
    • 在查询字符串里加入版本信息:
    示例:http://api.example.com/users/123?v=2
    • 通过媒体类型来指定版本信息
    Accept: application/vnd.github.v3+json
    Content-Type: application/vnd.github.v3+json 

    同样,版本编号也存在业界规范:语义化版本控制(Semantic Versioning)规范,网站地址:semver.org。版本编号由点号连接的3个数字组成,例如:1.2.3,分别表示主版本编号、次版本编号、补丁版本编号,版本编号的增加遵循下述规则:

    • 在对软件进行不向下兼容的变更时,增加主版本编号;
    • 在对软件进行向下兼容的变更或废除某些特定的功能时,增加次版本编号;
    • 如果软件的API没有发生变更,只是修正了部分bug,则增加补丁版本编号。

    按照版本编号增长的规则,WEB API的版本编号只需要标注主版本编号就可以了,因为次版本编号、补丁版本编号的增加都可以做到向下兼容,不会影响用户使用,唯有主版本编号增加才需要用户更新升级。除了标注版本信息之外,我们在对外发布WEB API时还需要设计好版本变更的策略,例如:老版本提供多久的过渡期、同时兼容多少个版本、特定版本的终止日期等等。

    • 总结

    何为优美?就是符合大众审美的,对于WEB API来说,就是符合标准规范的,这有利于降低用户学习和使用的成本,便于交流,不存在隐没成本。通常,业界存在的标准规范和事实标准都是经过实践筛选出来的,从遵循模仿开始,然后再找机会创新,而不是一上来就重复发明轮子。

    WEB API设计领域的标准规范就是URI、HTTP等,我们要最大程度地利用这些协议规范,让每个WEB API都是用户友好(易于使用)、技术友好(支持缓存、易于更改)的。除此之外,我们还需要考虑WEB API的健壮性,下一次我们再来谈一谈如何设计健壮的WEB API,欢迎大家找我讨论交流相关话题。今天先分享到这里,如果你觉得有价值,麻烦动动手指点下文 「 推荐 」按钮,让更多小伙伴可以看到,我也会更加有动力坚持分享。另外,老兵哥我后续还会分享职业规划、应聘面试、技能提升、影响力打造等经验,欢迎 关注 本专栏或歪信公主号 「 IT老兵哥 」。

    关注「 IT老兵哥 」,赋能程序人生!

    • 软技能-热门文章:(首发于公众号)
    1. “花式”裁员套路深,你知道吗?
    2. 遭遇裁员,如何渡过心理危机?
    3. 如何在寒冬中找到好工作?
    4. 2C 还是 2B,跟找工作有什么关系?
    5. 大公司 vs 小公司,你会选哪个?
    6. 记住这一点,不怕找不到好工作!
    7. 跳槽,跳还是不跳,该怎么跳?
    8. 程序员“求包养”攻略揭秘
    9. 很努力了,为什么我还在原地踏步?
    10. 从程序员到架构师,有捷径吗?
    • 硬技能-热门文章:
    1. 图解 Spring:HTTP 请求的处理流程与机制【1】
    2. 图解 Spring:HTTP 请求的处理流程与机制【2】
    3. 图解 Spring:HTTP 请求的处理流程与机制【3】
    4. 图解 Spring:HTTP 请求的处理流程与机制【4】
    5. 图解 Spring:HTTP 请求的处理流程与机制【5】
    6. 如何正确使用 Spring Cloud?【上】
    7. 如何正确使用 Spring Cloud?【中】
    8. 如何正确使用 Spring Cloud?【下】
    9. Spring 核心技术与产品理念剖析【上】
    10. Spring 核心技术与产品理念剖析【下】
  • 相关阅读:
    使用 requests 维持会话
    使用 requests 发送 POST 请求
    使用 requests 发送 GET 请求
    requests 安装
    使用 urllib 分析 Robots 协议
    使用 urllib 解析 URL 链接
    使用 urllib 处理 HTTP 异常
    使用 urllib 处理 Cookies 信息
    使用 urllib 设置代理服务
    按单生产程序发布
  • 原文地址:https://www.cnblogs.com/itlaobingge/p/12089657.html
Copyright © 2020-2023  润新知