likes
comments
collection
share

服务:如何做一个好的接口

作者站长头像
站长
· 阅读数 3

1 一般接口实现步骤

实现api的几个步骤

1, 实现 http 处理程序  implement HTTP Handlers
2, 实现测试集
3, 实现自定义类型
4, 实现 API,如 fka Swagger 
               Documentation  文档
                    collaboration  协作
 整合swagger-ui, Integrating swagger-ui
 为我们的API生成一个Go客户端 generating a go client

5,实现版本控制 versioning

6 真实案例 github REST API

一个好的接口需要投入大量精力去实现,从命名逻辑到数据处理,到高量的并发连接池处理,这里有他人总结的 restful api 的10条准则. 以备参考

1.1 务实 BE PRACTICAL

如果构建一个 REST API, 应该选择 接受 和 响应 JSON 格式数据.

1.2 有条不紊 BE METHODICAL

		 GET 
		 POST
			 当您发出 POST 请求时,服务通常希望您存储一些东西。
			您可能将在数据库中创建一个新行,在某处写一些东西或创建如果你愿意的话,从无到有。

			你可以发送使用多个内容将数据发送到 POST 方法
			类型:multipart/form-data 或 x-www-form-urlen 编码或原始(应用程序/json、文本/纯文本...等)。

			在构建 REST API 时,我会建议客户端将数据作为应用程序/json 发送。
			这样我们保持一致并本着 JSON 精神,还可以轻松发送真正复杂的请求的 JSON 数据。

			最后POST 操作不安全,因为它们确实改变服务器端的东西,它们是当然不是幂等的,
			因为使两个对同一端点的请求将导致不同的资源。

		 PUT
			 PUT 请求是最常见的在更新的上下文中使用事物。
			 它也可以用于创建新记录,但随后想法是客户应该是定义新资源的 ID。

			 所以要让您的生活更轻松只需使用PUT 当你想更新一个资源。 
			 PUT 当然不是安全操作,因为它使服务器端的变化,

			 但是,你会喜欢这个的,这是一个同样有效的操作

		 PATCH

			 补丁请求旨在用于更新
			再次使用资源,但与 PUT 不同,它应该更新
			只有更改的数据,而 PUT 可以和应该更新整个资源。这不安全也不是幂等的。

		 DELETE

大多数人使用GET,POST 和一些使用 PUT 和 DELETE。

很少做我看到正在使用 PATCH。我建议你使用所有您可以使用的 HTTP 方法,

因为这就是它们的设计目的。

您可以直接将任何 CRUD 操作映射到 POST、GET、PATCH 更新和 DELETE 删除。

我只是希望你不要曾经使用 GET 来创建或更新数据。

1.3 语义清晰 BE SEMANTICAL

这可能是你唯一一次听到我推荐某人是语义的。但在这种情况下重要的是正确命名事物。 我经常看到 API 文档很糟糕一切的命名约定。

在我看来,每个优秀的 REST APi 都应该易于理解你的普通json。 从端点名称到输入参数一直到 JSON 键。

使用名称而不是动词 使用复数而不是单数

使用名词的原因是因为当您发出 HTTP 请求时,正如我们在上面看到的,你已经在使用动词了。 每个 HTTP 方法(GET、POST、PUT、PATCH)都是英语中的动词。

您应该使用名词的原因是因为当您发出 HTTP 请求时,正如我们在上面看到的,

因此,使用双重动词是没有意义的——不是吗!?

如果您将 API 端点命名为 /getUsers 并且当您大声朗读时,

您正在发出 GET 请求以查看所有用户,这听起来很有趣。得到 /getUsers。没有意义。

我经常看到的另一个常见主题是端点名称使用单数而不是复数。那个当然不能再错了。 您希望您的 API 端点保持一致、简单且合乎逻辑。

如果你使用复数然后对于每个 HTTP 方法,您可以使用相同的 URI。

如果你做 GET /users 你列出了所有用户,如果你做 POST /users 你正在创建一个用户。 同样,相同的 URI,不同的 HTTP 方法(动词)。

如果您希望查看单个用户的详细信息,它会变得更酷 - 您请求 GET /users/:id 来获取信息。 如您所见 - 它仍然是相同的起始资源名称,只是更深入。

如果你用过单数然后 GET /user 意味着您希望获得一个用户并且您需要更多不同其他情况的 URI 复数只是更有意义。 推荐如下形式

    GET /users
    POST /users
    GET /users/23
    PUT /users/23
    DELETE /users/23
    GET /users/23/comments

不推荐如下形式

    GET /user/23
    GET /listAllUsers
    POST /user/create
    PUT /updateUser/23
    GET /userComments/23

关于端点命名的一些额外说明: 尝试使用单个词而不是多个词。如果您真的必须使用多个单词,请在它们之间设置连字符。 并且为了上帝请在 URI 中使用所有小写字母。

1.4 确保安全 BE SECURE

使用 HTTPs 只是提供了一个安全元素, 它使您的用户免受中间人攻击并加密在客户端和 API 之间的通信

苹果和谷歌等许多其他大玩家很快就会迫使你使用https 。

最好的部分是现在您可以免费获得 SSL 证书。使用服务像 AWS 和 Route 53、Azure 等价物或名为 Let's encrypt 的服务。

他们都工作得很好一个 API 没有使用任何形式的授权是错误的。如果你正在构建一个 API,你只需要能够控制谁可以访问它。

真的不必如此。你可以开始使用可以设置的不记名令牌 bearer tokens

在 2 分钟内。如果您不想,它们甚至不必连接到数据库。

什么时候你准备好了 你可以切换到更复杂的JWT 或 oAuth 等解决方案。

有许多为什么你应该有某种身份验证解决方案。

     从你这个事实开始可以控制对 API 的访问以及多少有人可以访问。
     如果出现问题,就像您的 API 被发送垃圾邮件、被黑客入侵或诸如此类
     您可以简单地关闭暴露的密钥。
     您还可以使用 API 密钥来跟踪如何API被集成了,有人也在做吗
    许多 API 调用,是客户端行为不正常。
    最后,您甚至可以使用 API 调用来收集有关您的客户端、用户和 API 的统计数据

发送网络上的超级敏感数据。我们谈过关于这一点以及使用 SSL 的重要性,以便通信是加密的。 那将是第一步。第二步是不返回数据这可能是敏感的,不会在应用程序或网站。

我说的是用户地址、电话号码、SSN 或任何其他形式的身份证明。

如果您不使用它,请不要发送它。如果你正在使用这个,请考虑确保访问该 API 并获取这些 API 的人 响应是您的数据的实际人返回。

我知道,这听起来很简单,但实际上人们会做各种疯狂的事情。主要是因为他们 在我们结束之前,我想谈谈 UUID身份证辩论。

我长期以来一直是 ID 的粉丝,因为它们更短更快,但增加了安全性和 在我看来,UUID 的隐私优势更为重要。UUID 更安全。

你仍然可以有一个 ID 列 在您的数据库中,它是自动递增的, 除了当您向 API 公开模型时,请尝试使用UUID。简短的推荐,但可以为您节省很多的头痛。

我想谈的最后一件事是通用基础设施安全。如果您使用的是 AWS 或 Azure,您有一个优势。 API 网关为您提供额外的安全性在防火墙和 DDoS 攻击检测方面。

采用如果可以的话。任何能让你能够阻止给定的 IP 或用户是你应该一直寻找的东西。 如果您在传统服务器上运行这里有 Apache 只是两个超级快速的提示关于如何拯救自己一些头疼。

  • 关于Apache 服务的安全性

第一个很简单:保持软件更新。 Apache、PHP、Composer 包、NPM 包...一切。确保您始终使用最新最好的软件。

第二点 默认情况下,Apache 会在每个请求中发送一个响应标头,从字面上告诉您的潜在攻击者您正在运行哪个版本的 Apache。

标头键称为 Server 和默认值可能类似于:Apache/2.4.1 (Unix)。 你想在这里做的是快速隐藏你的 Apache 版本。

只需打开:/etc/apache2/conf-enabled/security.conf 并设置对 Prod 的 ServerTokens 值。 之后运行 sudo systemctl restart apache2 , 你就完成了服务器比昨天更安全。恭喜。

打开 /etc/apache2/conf-enabled/security.conf 时您可以做的另一件快速的事情是 turn 关闭服务器签名。

只需将其设置为关闭,您就会更加安全。

作为一般规则,您应该每季度召开一次安全会议,讨论诸如如何 提高安全性,如何变得更好,如何保持安全。

你不想被黑客入侵、DDoSed 或其他什么相似的。相信我。

1.5 版本控制 有条理 BE ORGANIZED

有什么比对 API 进行版本控制更好的方式来保持井井有条? 现在我知道你已经读过很多次并想了一些类似“哦,我的 API 太小,它只一个客户端使用,所以我不会使用版本控制”。

我曾经是你们中的一员。但是现在变得更聪明并在您的设备上使用版本API。这是您可以尽早做出的最佳决定。

我总是对版本控制犹豫不决的原因是因为在我看来,从 v1 跳转到 v2 API 级别很少发生。 当我说很少时,我的意思是在科技领域——每年一次。

知道版本控制对我来说没有意义。但男孩是我错了。是的,大跳跃并不经常发生, 但如果你拥有正在使用或开发的平台或应用程序,您将进行较小的更新,但更频繁。

我是谈论有风险的模型、数据类型、结构或流程的每周或每月更新不更新应用程序或其他东西的人。 为此,如果您没有启用版本控制,每次执行 GIT 提交时,您都会汗流浃背。

你不仅要确保你的代码不会破坏任何东西或任何人,但您需要知道某个应用程序版本的行为方式。 一点都不好玩 信任我。

只需选择对您和您的团队最有意义的版本控制方案。 软件世界中常见的版本控制方案是:MAJOR.MINOR.PATCH。

在我看来PATCH 部分有点过多,但如果需要,您可以使用该模式。 我通常做 v1.1然后一直到 v1.9。因此,对于主要版本,您正在更改 API 的核心部分,

例如身份验证、核心模型、流程等。您通常会添加次要版本和补丁版本或删除较小的功能,以某种方式或类似的方式弄乱数据结构。

另一件可能让您感到困惑的事情是如何实际实施版本控制,因为有很多路。 您可以通过以下方式使用版本控制:URI 路径、请求标头、查询参数或内容协商

现在每个都有它的优点和缺点,但我建议使用 URL基于版本控制。

它是最有意义的,也是最容易理解的很多级别。从进行更新到了解使用哪个版本的所有内容 指向正确文档版本的 URL。

REST API 主要基于 URI,我认为我们应该保持这种传统并使用基于 URI 的版本控制。 一些例子包括:

            api.domain.com/v1/auth/login
            api.domain.com/v1.2/auth/login
            api.domain.com/v1.4.5/auth/login
            api.domain.com/v2/auth/login

尽早开始使用版本控制,以后您将避免出现问题并能够扩展您的 API

1.6 一致性 可持续性 BE CONSISTENT

您可能听说过这样的说法:“一致性是将平均转化为卓越的东西”。 这是真的不管你信不信,在生活中以及 API 设计中。优秀 API 的品质之一是它的一致性。

首先,我的目标是资源/模型的一致性,然后是其他领域,如命名、URI、HTTP代码和类似的。 正如我们现在所知,API 归结为资源。资源可以是用户、文章、书籍、产品...任何东西。

每个资源可以有多个属性、对象或数组。 资源是结构化的并基于您在数据库中的数据或其他一些业务逻辑。

保持资源响应一致是 API 成功的关键。你不能让你的端点返回完全不同的资源结构。 尽管这听起来很诱人,或者是优化事物的一种方式,但您应该避免这样做。

一种实现您的 API 的移动开发人员将遵循您提供的结构,就像圣经一样。 如果你发送在每个端点时间都有不同的东西,他或她将度过非常糟糕的一天,没有人想要那样。

所以尝试始终发送相同的资源结构。如果您没有数据,则将其作为空值或对象或数组发送。 让我们谈谈实际,假设我们有一个“文章”资源,有时该文章可能有意见 - 有时可能没有。

有时加载评论很有意义 - 有时它没有。这很好,但仍然尝试根据您的结构做出一致的响应。

当您收到一篇文章时,您也想像这样加载评论:

    {status:true
    message:“article list success”
    articles:[{"title":"","description":"",}{"title":}]
    }
    

因此,如果客户期望看他们的评论数组仍然会得到它,但它只会为空。

这样你就不是更改模型并删除对象/数组。你刚刚保存自己和别人一堆时间只是保持一致。

1.7 保持优雅

如果你正在构建 API,那么事情就会失败。没关系。这是预期的。 如果你不应该感到难过您的 API 有错误或问题。

如果您不提供详细信息,您应该感到难过的是它并确保你的 API 比其他人更聪明。

从顶部开始,我看到开发人员未能使用的最常见的东西之一是 HTTP 状态码。 在如果您不知道 HTTP 对于几乎所有可以想象的场景都有状态码。

你只需要知道使用哪一个并将其发送回客户端。 有超过 50 种不同的 HTTP 响应状态码它们都有独特的含义,应该在独特的场景中使用。

我不会通过所有这些但我们将列出最常见的组。我们有:

信息响应代码(以 1xx 开头) 
重定向响应代码(以 3xx 开头)
成功响应代码(以 2xx 开头) 
客户端错误响应代码(以 4xx 开头)
服务器错误响应代码(以 5xx 开头)

所以你真的拥有你需要的所有状态。 一切正常、未经授权、未找到、内部服务器我是茶壶的错误。

是的,最后一个是实际状态。只是证明制作 HTTPs 的人是终极笑话者!

在任何情况下,这些状态中的每一个都有其含义。这个意思是普遍接受和理解的。 所以来自中国的开发者和来自德国的开发者都会明白,

如果你发送一个状态401(未授权)您的意思是客户端没有发送正确的身份验证详细信息。

由于我们以 401(未经授权)的状态代码进行响应,因此每个人都明白这是一个客户端错误 它需要在客户端而不是 API 上修复。

我再次只给你一个例子,但想法是您应该在适当的情况下使用适当的 HTTP 状态代码。 使用它们将帮助您的 API普遍可理解、一致和标准。

即使 REST 不是标准,这也是其中之一你应该做的标准事情。

一旦 HTTP 状态代码为我们工作,我们需要向我们提供尽可能多的详细信息当事情不顺利时客户。

我们必须做很多事情来实现这一点。首先我们必须是能够预测我们的 API 可能会如何失败,其他人可能会做什么和不做什么,谁不会遵守规则等等......

所以第一步是进行强大而严格的数据验证,尤其是在创建内容之前。

在你拥有之后您必须检查数据是否有效! 这意味着检查 ID 是否确实存在,如果值是我们所期望的,

并且可以将它们存储在数据库中......完成所有这些并以适当的方式响应

状态码将使您的 API 易于使用。因此,假设您有一个接受 user_id 的端点 并获取用户配置文件。如果我们应用预测可能发生的策略,我们将执行以下操作:

    01 检查请求是否有 user_id 参数 - 如果没有响应 400 Bad Request
    02 检查系统中是否存在给定的 user_id - 如果没有响应 404 (Not Found)
    03 如果 user_id 返回结果 200 (OK)

如您所见,我们有多个故障保险柜,在所有这些保险柜中,我们都以正确且可理解的响应代码进行了响应。

最后,一旦我们设置了响应代码并且我们正在预测 API 可能会如何失败,我们只需需要尽可能的表达。

我知道我们的开发人员很难做到这一点,但相信我是这样的你能做的最好的事情。 当出现问题时,我们的 REST API 应该有一个通用的错误响应模型。

如果我们有,那么客户端开发人员可以依靠它为用户提供更深入的出了什么问题的解释。 假设用户在手机上输入了无效的电子邮件。不知何故被发送到 API,API 当然会触发验证和错误,

并会以 400(Bad Request)。除此之外,API 应该发送一个通用的错误响应模型, 允许客户端向最终用户显示任何和所有消息。因此,在这种情况下,您可能会返回一条错误消息:

“输入的电子邮件地址无效”。客户端可以读取它并将其显示给用户。 我们需要再次确保您可以涵盖从验证到服务器错误的任何场景。

要做到这一点,我们最好来有一个通用的错误模型,可以在任何情况下使用。我建议您使用以下内容:

    “title”:”Invalid data”,
    “message”:”The data you entered is not valid”,
    “errors”:[	{“field”: “email”...

错误的 JSON 结构很简单。我们有一个标题和信息,它提供了一个总体方向发生了什么。

很多时候,开发人员不会选择向最终用户显示完整的错误详细信息。他们可能会在 iPhone 上使用警报模式来显示我们的消息或标题。

但我们也发送了一个errors 数组,可以容纳带有特定消息的特定错误。 提供详细的错误信息将帮助您以及其他使用 API 的开发人员了解究竟出了什么问题。

甚至如果您没有向最终用户显示它们,您仍然可以在发出请求后查看它们或者, 如果您使用 Treblle 之类的服务。

所以请记住,请确保您尝试预测尽可能多的问题。 提供大量即使没有人使用这些细节并使用普遍理解的东西,也会详细说明事情失败的原因HTTP 响应代码的语言。

1.8 保持智能

这个比其他的更具哲学性,但在我看来,它是良好 REST API 的支柱之一。

如果您考虑 API 在现代平台、服务或应用程序中的作用,我们可以说 API 是整个操作的大脑。原因是您可能拥有的每个客户(iPhone 应用程序,Android 应用程序、网站、物联网设备)将使用相同的 API。

这意味着我们的 API 是最重要的部分 整个生态系统,我们的 API 应该能够处理所有事情。优雅地,如果我可以补充的话。

第一: 保护最重要的资产 数据库 应该做的是保护它最宝贵的资产——数据库。

这意味着它应该清理、清除并防止任何不良数据进入数据库。

做到这的方法是制作确保您验证从应用程序发送到客户端的所有内容并拒绝任何看起来不正确的内容。

好的。但是当我们拒绝某件事时,我们也应该向客户提供明确的理由, 让他们知道为什么发生了什么事,或者在这种情况下没有发生。

智能的 API将 将自行处理复杂的流程,不依赖客户的帮助 最简单的示例是在您的应用中注册用户。

对于所有应该是一个的客户他们发送数据并获得用户对象的 API 调用。 但是,在后台,API 应该处理任何和所有潜在的物流,

例如:在 MailChimp 时事通讯上注册该用户,将推送令牌存储到 Fire 基地, 向用户发送欢迎电子邮件等等......客户端不应调用多个 API 端点来那些事。

如果我们将所有内容打包到一个端点中,那么您可以随时轻松更改流程及时, 客户甚至不必意识到这一点。

只要你从他们那里得到你需要的数据 API 始终可以完全控制整个业务逻辑和流程。

当您处理多个平台或设备, 如 iPhone、Android 手机、计算机、电视等,API 应该能够适应他们。

这意味着 API 应该足够灵活以处理可能不同的输入来自所有客户, 并且仍然继续使客户能够完成他们的工作。一个很好的例子是可调整大小的图像。

如果您有一个 API 提供的内容包含您可能不想要的图像将 4000x4000 像素的图像发送到手机,

但也许你会到电视或网站。智能 API 会知道哪个客户端是访问该端点并返回优化的资源 它自己甚至允许客户要求特定的尺寸。

1.9 精益求精

如果不是快速的和优化,什么是好的 API!?你的 API 不应该是整个生态系统的痛点。

就这么简单。您可以做很多事情来确保提供一个好的 API 应该具备的性能和可扩展性。

  • 性能

在数据库层面 做到快速和优化 每当我听到有人说他们的 API 很慢时 9 到10倍​​它与数据库有关。

要么是糟糕的数据库设计,要么是复杂的查询,要么是速度慢 基础设施甚至缺乏缓存。您应该始终优化数据库结构、查询、索引以及与数据库交互的所有其他内容。

一旦你处理了接下来的事情重点是确保尽可能提供缓存数据。 缓存数据库查询将有时会从字面上减少 100% 的加载时间。

因此,如果您有一个 API 端点向用户显示他们的profile 或类似的东西, 我相信它不会每 5 分钟更改一次。聪明点,缓存数据。您的用户调用该 API 的客户会喜欢它。

另一个性能影响因素是您通过 API 发送给客户端的大量数据。 制作确保您的资源和模型只返回客户需要的数据,而不是整个数据包。

如果您正在返回一组项目,也许您不需要完整的模型细节和关系 - 每次。 如果这会加快速度,那就去吧,但总是尽量与返回的模型保持一致回应。

这意味着如果您没有加载关系,则返回一个空数组,如果您没有加载计数返回 0 等等。 构建出色的 REST API 时,一致性是关键

  • 压缩 您可以做的另一件非常简单的事情来减少响应大小并提高性能是启用压缩。 最流行的一种是 Gzip 压缩。

在 Apache 上启用默认情况下带有一个名为 mod_deflate 的模块。 启用后,您通常可以看到 Accept-Encoding 的响应标头设置为 gzip、compress 之类的内容。

即使它默认启用您仍然需要将其“应用”到您的 API 响应中。 为此,我们必须在默认值中添加一些规则apache配置。

所以让我们像这样打开配置文件:

   sudo nano /etc/apache2/conf-available/000-default.conf 
   

并添加这个美丽的东西:查看要点

这告诉 Apache 对各种 mime 类型应用 Gzip 压缩, 例如 application/javascript、application/json 等。

一旦我们保存了这个,我们只需要通过输入 sudo systemctl restart 来重新启动 Apache/apache2,我们完成了。您的 API 响应现在比以前小 50-75%。那有多容易?!

1.10 健壮性 保持周全

最后一篇我会很短,因为我们在 REST 上已经过火了(永远不会过时),但我没有想淡化它的重要性。

对于我们后端开发人员来说,开发 API 的过程是相当美好的孤独。 主要是你,一堆代码和你最喜欢的测试工具。

你可能认为工作完成时您编写最后一行代码并合并到生产分支中。但事实并非如此。 对于许多其他人旅程才刚刚开始。

有很多人只会在你完成你的工作后才开始做他们的工作。 为了他们为了能够做好工作,您需要以多种方式准备 API。

你必须确保它是工作,它有很好的文档记录,更重要的是你必须准备好提供集成支持。

不管你写的文档有多好,总会有问题、疑惑和问题在整合过程中及以后设身处地为他人着想, 努力让他们的工作更轻松。

构建好的 API,遵循我们的规则在这里定义,编写出色的文档并为其他所有人服务。 考虑使用可以帮助您应对构建、交付和运行 API 的许多挑战的工具。

您猜对了,其中一个工具是 Treblle。只需将 Treblle 添加到您的 API,您就会自动生成 具有 OpenAPI 规范支持的文档。

然后你会得到实时监控和记录,这样你就可以看到其他人正在做什么并更好地帮助他们。 最后,您甚至会获得基于 API 的高质量核心这些相同的规则。

1.11 示例

  • 创建 映射 mapping

      type Action func(out http.ResponseWriter, req * router.Request)
    
      type Resource map[string]Action
    
      var resources = map[string]Resource{
        "users": map[string]http.Handler {
          "show": users.Show,
          "list": users.List,
          "listfavorites": users.ListFavorites,
        }
        "sessions": map[string]http.Handler {
          "create": sessions.Create,
          "delete": sessions.Delete,
        }
      }
    
  • 定义路由

       r := router.New(router.Configure())
       r.Get("/:resource", ListAction)
       r.Get("/:resource/:id", ShowAction)
       r.Get("/:resource/:id/:child", ListChildAction)
       r.Delete("/:resource/:id", DeleteAction)
    
  • 调用适当的接口 handler

    func ListAction(out http.ResponseWriter, req * router.Request) {
    	resource, exists := resources[req.Param("resource")]
    	if exists == false {
    	// 没有返回值
    	return
    }
    action, exists := resource["list"]
    if exists == false{
    // 没有返回值
    return
        }
        action(out, req)
    
        }
    

    通过重构 handler函数来 创建另一个 注册路由路径为:

             "/:resource/:id/:child"
    

url路径为:

            /users/48/favorites
            

这将查找users资源的动作 listfavorites

	func ListChildAction(out http.ResponseWriter, req *router.Request) {

	  RestDispatch(out, req, "list"+req.Param("child"))
	}

	func RestDispatch(out http.ResponseWriter, req *router.Request, actionName string) {
	  resource, exists := resources[req.Param("resource")]
	  if exists == false {
	    // 没有返回值
	    return
	  }
	  action, exists := resource[actionName]
	  if exists == false {
	    //  没有返回值
	    return
	  }
	  action(out, req)
	}
  • 使用反射提升 映射

    type Action func(out http.ResponseWriter, req *router.Request)
    
    type Resource map[string]Action
    
    var resources = make(map[string]Resource)
    
    func Start() {
    	r := router.New(router.Configure())
    	r.Get("/:resource", ListAction)
    	r.Get("/:resource/:id", ShowAction)
    	r.Get("/:resource/:id/:child", ListChildAction)
    	r.Delete("/:resource/:id", DeleteAction)
    
    	registerActions(users.List, users.Show, users.ListFavorites)
    	registerActions(sessions.Create, sessions.Delete)
    

    }

    非线程安全,建议在init调用 1 实现一个函数 2 获取它的全名,比如 github.com/karlseguin/system/users.List 3 提取 users.List 4 在users 资源路径 注册 list 动作

    func RegisterAction(actions ...Action) {
    	  for _, action := range actions {
    	    fullName := runtime.FuncForPC(reflect.ValueOf(action).Pointer()).Name()
    	    relevant := strings.ToLower(fullName[strings.LastIndex(fullName, "/")+1:])
    	    parts := strings.Split(relevant, ".")
    	    if len(parts) != 2 {
    	      panic("action " + fullName + " should be in the form of package.name")
    	    }
    
    	    resourceName, actionName := parts[0], parts[1]
    	    resource, exists := Resources[resourceName]
    	    if exists == false {
    	      resource = make(Resource)
    	      Resources[resourceName] = resource
    	    }
    	    resource[actionName] = action
    	  }
    	}
    

    如此可进一步强制 一致的命名,并减少一些魔术字符串

小结

我们在设计接口时,需要考虑到了整个团队。

希望我能够简化和解释您可能遇到的一些常见疑问和担忧在构建 REST API 时。 再次,我必须注意,REST 不是标准,所以没有人可以告诉你你在做什么,它是否错了。

但请考虑一下:作为开发人员,我们每天都在寻找模式以使我们的代码更好、更漂亮、 更高效,为什么不对 API 做同样的事情。

如果我们开始遵循一些模式,我们都会很享受更健康、更漂亮的生态系统。

“权力越大,责任越大”。