Limyee 电商平台提供了一组强大的进程 API,允许开发人员与其数据进行交互。这些 API 通过 API 服务访问,该服务将在提供类类型时返回实现 IApi 接口的类实例。作为开发人员,您还可以创建自己的 API,并与访问平台进程 API 相同方式公开给 API 服务。
何时应创建自己的 API?
API 是我们与在我们的平台的开发人员签订的开发合同。我们同意,在未提供充分通知的情况下,我们不会使用破坏现有集成或自定义的方式更改 API。还有许多供应商、独立软件开发人员和合作伙伴为平台创建各种自定义组件,这些组件可能希望为其产品公开类似的 API。因此,将 API 订阅 API 服务是有意义的,因为它将允许开发人员用与其他 API 完全相同的方式访问自定义 API。
如果要创建不用重新分发的小型自定义项,或者创建没有面向公众 API 的可分发组件,则无需执行此操作。此功能主要面向可分发组件和核心平台 API,但您可以随时使用它来通过 API 服务公开自己的 API。
将您的 API 暴露给 Limyee 电商平台
实现正确的接口
在设计 API 时,需要考虑一些注意事项,以避免以后出现问题。正如您在注册接口时所看到的,仅当应用程序在插件初始化之前(即应用程序启动或插件被某些内部事件强制重新加载时)才会创建 API 实例。这意味着您的 API 类应该是无状态的,或者至少只在实例级别存储数据,该实例级别适用于进程中与 API 的所有交互,例如其他依赖服务或常量值。虽然 API 不必是单例类,但开发应可以简单地获取它们。这个类也不能是静态的。
您可以通过实现 IApi 接口使类准备好订阅 API 服务。实现此接口很容易,因为它没有要实现的属性或方法,因此除了将接口添加到类定义之外,您不需要额外的工作。
在此示例中,我们有一个名为 PersonService 的 API 要公开,它公开了一个方法来获取随机名称。为了准备要包含在 API 服务,我们添加了 IApi 接口:
public class PersonService : Limyee.Extensibility.Api.IApi { public string GetRandomName() { return "Limyee"; } }
如果您使用自己的接口设计所有 API,并希望能够在加载 API 时引用自定义接口,则可以使用 IApi 接口而不是类实现本身来扩展接口。然后,您将能够使用自定义接口类型或具体类类型在服务中引用 API。
public interface IPersonService : Limyee.Extensibility.Api.IApi { string GetRandomName(); } public class PersonService : IPersonService { public string GetRandomName() { return "Limyee"; } }
注册接口
一旦你有了一个以某种方式实现 IApi 类,你就可以把它注册到平台上。这是通过 IApiDefinition 插件类型完成的。如果您还不熟悉插件模型,建议您查看有关插件的主题。
除了 IPlugin 成员之外,IApiDefinition 只添加了一个名为 RegisterApi 的方法,该方法为您提供了 IApiController。通过控制器,您可以通过提供实现 IApi 类的实例来注册 API。
public void RegisterApi(IApiController controller) { controller.Add(new PersonService()); }
使用您的 API
首先编译您的项目,并将项目 DLL 部署到 Limyee 电商平台的 bin 文件夹和作业服务文件夹。由于 IApiDefinition 是一个插件,因此您需要先在“管理”区域中启用它,然后才能在代码中使用它。假设它没有实现其他插件类型,您可以在管理 > 扩展中找到它。
在尝试使用 API 以避免任何错误之前,请务必了解 API 何时在产品生命周期中注册。平台在插件初始化之前注册所有 API。这意味着除了在应用程序运行时正常使用之外,您还可以在插件 Initialize 方法或任何 AfterInitialization 方法中使用已注册的 API。您应该避免在插件生命周期的 BeforeInitialization 中使用任何 API,因为无法保证 API 已注册。
若要使用 API,请使用 Get<>() 方法从 API 进程服务检索它,方法与获取内置进程 API 的方式相同。除了具体类之外,API 服务还会跟踪 API 及其继承链,因此,如果您的 API 使用实现 IApi 的抽象类,则可以按抽象类类型检索 API。同样,如果为扩展了 IApi 的类使用接口,则可以按自定义接口类型检索它。
var personAPI = Limyee.Extensibility.Apis.Get<PersonService>();
或者因为我们的PersonService实现了扩展 IApi 的 IPersonService,这也是合适的:
var personAPI = Limyee.Extensibility.Apis.Get<IPersonService>();
注意:使用自定义接口或抽象类扩展 IApi 时,需要记住 API 服务只能将 1 个 API 关联到一个类型,因此您不能有多个使用同一抽象类或接口的 API。如果您的 API 共享一个公共接口,则该接口的类实现应实现 IApi。
例如,如果所有 API 都使用 IRepository 接口,则 IApi 需应用于类实现:
public interface IRepository { } public class PersonRepository : IRepository, IApi { }
如果接口扩展了 IApi 接口,则可以将其应用于接口,但接口本身仍应只有一个实现:
public interface IRepository { } public interface IPersonRepository : IApi { } public class PersonRepository : IPersonRepository{ }
在使用 API 之前,最好将 API 的任何用法包装在空检查中,因为 IApiDefinition 插件可能已在管理中禁用(这不适用于核心进程 API,因为它们无法禁用)。
var personAPI = Limyee.Extensibility.Apis.Get<IPersonService>(); if (personAPI != null) { //Intract with personAPI }
示例
您可以下载以下示例作为指南。编译后,部署到您的平台 bin 文件夹,并且启用插件 SampleAPIDefinition(管理 > 扩展),当插件初始化时,它会将“随机名称:Limyee”,写入事件日志(管理 > 监测 > 事件)。
using Limyee.Extensibility.Api; using Limyee.Extensibility.Api.Version1; namespace Samples { #region Our API Interface public interface IPersonService : IApi { string GetRandomName(); } #endregion #region Our API Class Implementation of IAPI public class PersonService : IPersonService { public string GetRandomName() { return "Limyee"; } } #endregion #region IApiDefinition Plugin public class SampleAPIDefinition : IApiDefinition { #region IApiDefinition Members public void RegisterApi(IApiController controller) { //Invoke add on the controller and provide an instance of your class controller.Add(new PersonService()); } #endregion #region IPlugin Members public string Name { get { return "API 定义 示例"; } } public string Description { get { return "API 注册插件示例"; } } public void Initialize() { var personAPI = Limyee.Extensibility.Apis.Get<IPersonService>(); var eventLogApi = Limyee.Extensibility.Apis.Get<IEventLog>(); if (personAPI != null && eventLogApi != null) { //Simply write to the event log eventLogApi.Write("随机名称:" + personAPI.GetRandomName(), new EventLogEntryWriteOptions()); } } #endregion } #endregion }