Web Api响应版本控制解决方案?

Lev*_*kon 9 c# asp.net-mvc asp.net-mvc-4 asp.net-web-api

在您认为这是重复之前,请花一点时间.当我在版本控制问题上研究Web Api时,一切都关注版本化控制器和关于在url与头文件中指定版本的最佳实践.

我想弄清楚的是版本输出响应的最佳方法是什么,所以当我推出版本2时,我不会破坏版本1客户端.

让我们说,我为一个网站套件提供不断变化的DAL,为网站和其他服务提供信息.我正在开发一个新的Web Api项目,该项目应该具有符合版本化模式的响应.

我的问题是,在版本控制器之前和未版本化的DAL之前,在Web Api项目中实现版本控制的成熟解决方案/最佳实践是什么?

我想出了一个解决方案,其中包含一个额外的版本化存储库层和一个额外的版本化模型层,因此版本控制器使用版本化存储库来使用版本化模型.我已经设置了Automapper来映射未版本化的域模型(从DAL)到版本化的模型.但是这个设置的继承缺陷是,我必须更新每个新版本的所有地图; 一个指数级增长的问题.

一定有更好的方法.谢谢!

Kar*_*ath 2

根据我的经验,我们找到的最干净(但不是最简单)的解决方案分为 5 个部分:

  1. 拥有权威的数据模型和始终保持最新的后端:DAL/数据库/服务。
  2. 拥有系统可以理解的特定于版本的数据:多数据模型。
  3. 确保正确识别和跟踪版本之间的更改,并且更改是可逆的。必须明确定义规则来处理该问题(这可能更难):转换器。
  4. 让客户端明确告诉您他们使用哪个版本:查询字符串/标头。
  5. 拥有始终保持最新的权威业务逻辑,但知道如何在不同数据版本之间移动 - 向后兼容:控制器。

例如,我们拥有的数据模型是供应商。

(1)后端(即 DAL/数据库)位于 V5。业务逻辑(即服务)也处于 V5。

(2)但是,我们需要同时为 V1、V2、V3、V4 上的客户供应商以及 V5 上的最新客户提供服务。让我们将数据模型设为 V1 到 V5:SupplierV1、SupplierV2...(您可以使用命名空间或其他方式来区分它们)

(3)您需要定义转换器并管理它们。它们必须处理数据模型版本之间的前向和后向兼容性。这可以通过策略模式、lambda、带有 DI 转换器的管理器等来完成。您需要处理V1->V2->V3->V4->V5,还需要处理V5->V4->V3->V2 ->V1

转换器中的规则是最难的部分。有时很简单 - 新字段的默认值。有时您需要运行一些业务逻辑。有时您需要转换/合并现有数据;你必须确保它是可逆的!例如,如果 V1 中的值是混合大小写,并且您在 V2 中将其转换为大写...您将无法将其返回到 V1,因为您不知道哪些字符是大写和小写。您可以通过两种方式处理:

  • V1 保持大写。毕竟,只有 V2 需要它,V1 可能可以处理所有大写字母(显然不适用于按键)。
  • 将旧值保留在 V2 的字段中。例如,如果您决定将字段 State 设为大写,则可以保留大小写混合的 OriginalState,并在返回到 V1 时将其复制到 State。

正如您所看到的,您需要认真考虑这些问题,而且这通常不是微不足道的。

(4)然后,在控制器中,您需要知道客户端使用哪个版本,以便在需要时进行控制器进出的转换。为此,您可以使用查询字符串、标头(例如 X-API-Version 或 Accept,但要注意某些主机/代理会删除其中的一些)、post 参数等。

(5)最后,当控制器收到数据模型时,需要检查客户端发送的版本(假设是V2)并将其升级到后端的最新版本(V5)。然后正常使用,之后如果需要回传数据,需要降级到客户端版本(V2)。为此,您可以执行自定义绑定、自定义请求操作、自定义操作结果等。例如(请自动执行此操作):

public IHttpActionResult DoSomethingWithSupplier(JToken supplier) // Receiving Json Supplier
{
   // TODO: Get the client version type, for example in the X-API-Version header/query strings
   // (beware, some proxy/hosts strips some headers)
   // Type clientType = ...

   var clientSupplier = JsonConvert.DeserializeObject(supplier.ToString(), clientType);

   // You should probably detect latest version automatically (instead of typeof)
   var latestSupplier = VersionManager.Upgrade(clientSupplier, clientType, typeof(SupplierV5));

   outputSupplier = DoSomething(latestSupplier);

   // You should probably detect latest version automatically (instead of typeof)
   var clientOutputSupplier = VersionManager.Downgrade(outputSupplier, typeof(SupplierV5), clientType);

   return Ok(clientOutputSupplier);
}
Run Code Online (Sandbox Code Playgroud)

这是向您展示这个想法的一种非常粗略的方式。这是我们在我们的一个系统中所做的事情。您可以让管理器检测类型和版本并自行处理转换,您可以通过依赖项注入动态加载程序集/转换器,您可以在自定义绑定/请求操作中自动执行大部分转换,等等。

注意:您可能还需要考虑第(6)部分。要实际将数据库中的客户端数据更新到 V5,您可以在迁移到 V5(数据批量迁移)时执行此操作,或者在运行时执行此操作。当您收到供应商 V1 时,您可以从数据库加载该数据(仍然是 V1 数据),升级到 V5,然后保存更新的数据,所有这些都在转换器中。这意味着您现在可以动态迁移后端。显然,这并不像听起来那么容易,因为您需要在同一数据库中支持两个版本,但根据您拥有的更改或数据的类型,可能会很适合您。