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

时间:2013-03-25 14:57:49

标签: 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)到版本化的模型。但是这个设置的继承缺陷是,我必须更新每个新版本的所有地图;一个指数级增长的问题。

必须有更好的方法。谢谢!

2 个答案:

答案 0 :(得分:2)

根据我的经验,我们发现的最简洁(但不是最简单)的解决方案分为5个部分:

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

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

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

    (3)您需要定义转换器并对其进行管理。它们必须处理数据模型版本之间的前向和后向兼容性。这可以通过策略模式,lambdas,具有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);
    }
    

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

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

答案 1 :(得分:0)

我建议您对模型进行版本控制。您可以通过命名空间来执行此操作以简化操作。事实上,NServiceBus为它的消息传递做了类似的事情。以下是他们的示例:http://support.nservicebus.com/customer/portal/articles/894151-versioning-sample