使用自定义端点扩展现有 API

Posted

技术标签:

【中文标题】使用自定义端点扩展现有 API【英文标题】:extend existing API with custom endpoints 【发布时间】:2020-02-25 15:27:47 【问题描述】:

我正在为多个客户创建一个 API。每个客户都使用/users 等核心端点,但有些端点依赖于个人定制。因此,用户 A 可能需要一个特殊的端点 /groups,而其他客户将没有该功能。 顺便说一句,由于这些额外的功能,每个客户也会使用自己的数据库架构。

我个人使用 NestJs(引擎盖下的 Express)。所以app.module 目前注册了我所有的核心模块(有自己的端点等)

import  Module  from '@nestjs/common';

import  UsersModule  from './users/users.module'; // core module

@Module(
  imports: [UsersModule]
)
export class AppModule 

我认为这个问题与 NestJs 无关,那么理论上你会如何处理呢?

我基本上需要一个能够提供基本系统的基础设施。不再有核心端点,因为每个扩展都是唯一的,并且可能有多个 /users 实现。在开发新功能时,不应触及核心应用程序。扩展应该自己集成或者应该在启动时集成。核心系统没有端点,但会从这些外部文件扩展。

我想到了一些想法


第一种方法:

每个扩展都代表一个新的存储库。定义包含所有扩展项目的自定义外部文件夹的路径。此自定义目录将包含一个文件夹 groups 和一个 groups.module

import  Module  from '@nestjs/common';

import  GroupsController  from './groups.controller';

@Module(
  controllers: [GroupsController],
)
export class GroupsModule 

我的 API 可以遍历该目录并尝试导入每个模块文件。

优点:

    自定义代码远离核心存储库

缺点:

    NestJs 使用 Typescript,所以我必须先编译代码。我将如何管理 API 构建和自定义应用程序的构建? (即插即用系统)

    自定义扩展非常松散,因为它们只包含一些打字稿文件。由于他们无权访问 API 的 node_modules 目录,我的编辑器会显示错误,因为它无法解析外部包依赖项。

    某些扩展程序可能会从另一个扩展程序获取数据。也许组服务需要访问用户服务。这里的事情可能会变得棘手。


第二种方法: 将每个扩展保存在 API 的 src 文件夹的子文件夹中。但是将此子文件夹添加到 .gitignore 文件中。现在您可以将扩展保留在 API 中。

优点:

    您的编辑器能够解决依赖关系

    在部署您的代码之前,您可以运行构建命令,并将拥有一个单一的分发版

    您可以轻松访问其他服务(/groups需要通过id查找用户)

缺点:

    在开发时,您必须将存储库文件复制到该子文件夹中。更改某些内容后,您必须将这些文件复制回来并用更新的文件覆盖您的存储库文件。

第三种方法:

在外部自定义文件夹中,所有扩展都是成熟的独立 API。您的主 API 将只提供身份验证内容,并可以充当代理将传入请求重定向到目标 API。

优点:

    可以轻松开发和测试新的扩展程序

缺点:

    部署会很棘手。您将拥有一个主 API 和 n 个扩展 API,它们启动自己的进程并监听端口。

    代理系统可能很棘手。如果客户端请求/users,则代理需要知道哪个扩展 API 侦听该端点,调用该 API 并将该响应转发回客户端。

    为了保护扩展 API(身份验证由主 API 处理),代理需要与这些 API 共享机密。因此,如果代理提供了匹配的密钥,扩展 API 只会传递传入的请求。


第四种方法:

微服务可能会有所帮助。我从这里拿了一个指南https://docs.nestjs.com/microservices/basics

我可以为用户管理、组管理等提供一个微服务,并通过创建一个调用这些微服务的小型 api/网关/代理来使用这些服务。

优点:

    可以轻松开发和测试新的扩展

    分离的关注点

缺点:

    部署会很棘手。您将拥有一个主 API 和 n 个微服务,它们启动自己的进程并监听端口。

    如果我想定制它,我似乎必须为每个客户创建一个新的网关 api。因此,我每次都必须创建一个自定义的消费 API,而不是扩展应用程序。这不会解决问题。

    为了保护扩展 API(身份验证由主 API 处理),代理需要与这些 API 共享机密。因此,如果代理提供了匹配的密钥,扩展 API 只会传递传入的请求。

【问题讨论】:

这可能会有所帮助github.com/nestjs/nest/issues/3277 感谢您的链接。但我认为我的代码中不应该有自定义扩展。我会检查微服务是否能解决问题docs.nestjs.com/microservices/basics 我认为您的问题与授权有关,而不是与休息有关。 @adnanmuttaleb 你介意解释一下为什么 = 吗? 【参考方案1】:

有几种方法可以解决这个问题。您需要做的是找出最适合您的团队、组织和客户的工作流程。

如果这取决于我,我会考虑每个模块使用一个存储库,并使用像 NPM 这样的包管理器和私有或组织范围的包来处理配置。然后设置构建发布管道,在新构建上推送到包存储库。

这样,您只需要主文件和每个自定义安装的包清单文件。您可以独立开发和部署新版本,并且可以在需要时在客户端加载新版本。

为了增加流畅度,您可以使用配置文件将模块映射到路由并编写通用路由生成器脚本来完成大部分引导。

由于包可以是任何东西,因此包中的交叉依赖项可以轻松工作。在变更和版本管理方面,您只需要遵守纪律。

在此处阅读有关私有包的更多信息: Private Packages NPM

现在私有 NPM 注册需要花钱,但如果这是一个问题,还有其他几种选择。请查看这篇文章以了解一些替代方案 - 免费和付费。

Ways to have your private npm registry

现在,如果您想创建自己的管理器,您可以编写一个简单的服务定位器,它接收一个包含必要信息的配置文件,以便从存储库中提取代码、加载它,然后提供某种方法检索它的实例。

我为这样的系统编写了一个简单的参考实现:

框架:locomotion service locator

检查回文的示例插件:locomotion plugin example

使用框架定位插件的应用程序:locomotion app example

您可以通过使用 npm install -s locomotion 从 npm 获取它来解决此问题,您需要使用以下架构指定 plugins.json 文件:


    "path": "relative path where plugins should be stored",
    "plugins": [
         
           "module":"name of service", 
           "dir":"location within plugin folder",
           "source":"link to git repository"
        
    ]

示例:


    "path": "./plugins",
    "plugins": [
        
            "module": "palindrome",
            "dir": "locomotion-plugin-example",
            "source": "https://github.com/drcircuit/locomotion-plugin-example.git"
        
    ]

像这样加载它: const loco = require("运动");

然后它返回一个将解析服务定位器对象的承诺,该对象具有获取服务的定位器方法:

loco.then((svc) => 
    let pal = svc.locate("palindrome"); //get the palindrome service
    if (pal) 
        console.log("Is: no X in Nixon! a palindrome? ", (pal.isPalindrome("no X in Nixon!")) ? "Yes" : "no"); // test if it works :)
    
).catch((err) => 
    console.error(err);
);

请注意,这只是一个参考实现,对于严肃的应用来说不够健壮。但是,该模式仍然有效,并显示了编写这种框架的要点。

现在,这需要扩展支持插件配置、初始化、错误检查,也许添加对依赖注入等的支持。

【讨论】:

谢谢。出现了两个问题,似乎我们依赖 npm 并且必须设置一个自托管注册表。第二件事是私有 npm 不再免费。我希望找到一个基本的技术解决方案。但是 +1 的想法:) 为这种系统添加了一个基本解决方案的参考实现。【参考方案2】:

我会选择外部包选项。

您可以将您的应用程序构建为具有packages 文件夹。我会在该文件夹中对外部包进行 UMD 编译,这样编译后的打字稿不会对包有任何问题。所有包都应该在每个包的根文件夹中有一个index.js 文件。

您的应用可以使用fsrequire 将所有包index.js 运行到您的应用中,从而在包文件夹中循环运行。

然后依赖安装是你必须照顾的事情。我认为每个包上的配置文件也可以解决这个问题。您可以在主应用程序上使用自定义 npm 脚本,以在启动应用程序之前安装所有包依赖项。

这样,您只需将新包复制粘贴到包文件夹中并重新启动应用程序即可将新包添加到您的应用程序。您编译的打字稿文件不会被触及,您不必为自己的包使用私有 npm。

【讨论】:

感谢您的回复。我认为这听起来像是 @ Espen 已经发布的解决方案,不是吗? @hrp8sfH4xQ4 是的,延伸。在阅读了您关于不想使用npm 的评论后,我添加了它。以上是您可以避免使用私人 npm 帐户的解决方案。此外,我相信您不需要添加组织外部人员创建的包。对吗? 顺便说一句。但是支持它也太棒了……不知何故…… 如果您需要添加第三方包的选项,那么npm 是这样做的方式或任何其他包管理器。在这种情况下,我的解决方案是不够的。 如果你不介意我想等着收集尽可能多的方法

以上是关于使用自定义端点扩展现有 API的主要内容,如果未能解决你的问题,请参考以下文章

Wordpress 使用自定义端点 rest api 上传多个图像(离子作为最终用户)

使用自定义Wordpress API端点更新WooCommerce产品

Auth0 端点 API 的自定义实现?

Wordpress Rest Api 在自定义端点上使用 POST 更新内容

WP Rest API 自定义端点在 GET 和 POST 上返回 false

WordPress 自定义 API 端点 POST 请求在 React 中失败