您的Web服务版本控制最佳实践是什么?

时间:2010-01-20 13:20:58

标签: web-services architecture versioning

我们有2个独立的产品需要通过Web服务相互通信。 支持API的自动化的最佳实践是什么?

我从2004年开始this article声称没有实际标准,只有最佳做法。更好的解决方案?你如何解决WS版本问题?

问题描述

系统A

客户端

class SystemAClient{
    SystemBServiceStub systemB;
    public void consumeFromB(){
        SystemBObject bObject = systemB.getSomethingFromB(new SomethingFromBRequest("someKey"));

    }
}

服务

class SystemAService{
    public SystemAObject getSomethingFromA(SomethingFromARequest req){
        return new SystemAObjectFactory.getObject(req);
    }
}

可转让对象

版本1

class SystemAObject{
     Integer id;
     String name;
     ... // getters and setters etc;
}

第2版

class SystemAObject{
     Long id;
     String name;
     String description;
     ... // getters and setters etc;
}

请求对象

版本1

class SomethingFromARequest {
     Integer requestedId;
     ... // getters and setters etc;

}

第2版

class SomethingFromARequest {
     Long requestedId;
     ... // getters and setters etc;

}

系统B

客户端

class SystemBClient{
    SystemAServiceStub systemA;
    public void consumeFromA(){
        SystemAObject aObject = systemA.getSomethingFromA(new SomethingFromARequest(1));
        aObject.getDescription() // fail point
        // do something with it...
    }
}

服务

class SystemBService{
    public SystemBObject getSomethingFromB(SomethingFromBRequest req){
        return new SystemBObjectFactory.getObject(req);
    }
}

可转让对象

版本1

class SystemBObject{
     String key;
     Integer year;
     Integer month;
     Integer day;

     ... // getters and setters etc;
}

第2版

class SystemBObject{
     String key;
     BDate date;
     ... // getters and setters etc;
}

class BDate{
     Integer year;
     Integer month;
     Integer day;
     ... // getters and setters etc;

}

请求对象

版本1

class SomethingFromBRequest {
     String key;
     ... // getters and setters etc;
}

第2版

class SomethingFromBRequest {
     String key;
     BDate afterDate;
     BDate beforeDate;
     ... // getters and setters etc;
}

失败方案

如果版本1 系统A 客户端调用系统B 服务 版本2 >它可以失败:

  • SystemBObject上遗漏的方法(getYear()getMonth()getDay()
  • 未知类型BDate

如果版本2 系统A 客户端调用系统B 服务 版本1 >可能会失败:

  • BDate上的未知类型SomethingFromBRequest(客户端使用B版本1无法识别的较新B请求对象)
  • 如果系统A客户端足够聪明,可以使用请求对象的版本1,则SystemBObject对象(getDate())上缺少方法可能会失败

如果版本1 系统B 客户端调用系统A 服务 版本2 >它可以失败:

  • SystemAObject上输入错误匹配或溢出(已返回Long,但预计为Integer

如果版本2 系统B 客户端调用系统A 服务 版本1 >可能会失败:

  • SystemARequest上输入错误匹配或溢出(请求Long而不是Integer
  • 如果请求以某种方式传递,则转换问题(存根为Long但服务返回Integer并非在所有WS实现中兼容niksecarily)

可能的解决方案

  1. 推进版本时使用数字:例如SystemAObject1SystemBRequest2等,但缺少用于匹配源/目标版本的API
  2. 在签名中,传递XML而不是对象(yuck,传递XML中的转义XML,双序列化,反序列化/解析,解析)
  3. 其他:例如Document / literal / WS-I有补救措施吗?

4 个答案:

答案 0 :(得分:23)

我更喜欢Salesforce.com的版本控制方法。每个版本的Web服务都以以下格式获取不同的URL:

http://api.salesforce.com/{version}/{serviceName}

因此,您将拥有如下所示的Web服务URL:

http://api.salesforce.com/14/Lead

http://api.salesforce.com/15/Lead

依旧......

通过这种方法,您可以获得以下好处:

  1. 您总是知道您正在与哪个版本对话。

  2. 保持向后兼容性。

  3. 您不必担心依赖性问题。每个版本都有完整的服务。您只需要确保不要在调用之间混合版本(但这取决于服务的使用者,而不是您作为开发人员)。

答案 1 :(得分:4)

解决方案是避免对您的类型进行不兼容的更改。

以SystemBObject为例。您描述了此类型的“版本1”和“版本2”,但它们根本不是同一类型。对此类型的兼容更改仅涉及添加属性,而不是更改任何现有属性的类型。您假设的“版本更新”违反了这两个限制。

通过遵循一个guildeline,您可以避免所描述的所有问题。

因此,如果这是版本1中的类型定义

class SystemBObject{  // version 1
    String key;  
    Integer year;  
    Integer month;  
    Integer day;  

    ... // getters and setters etc;  
}  

然后,这不能是你在v2中的类型定义:

// version 2 - NO NO NO 
class SystemBObject{ 
    String key; 
    BDate date; 
    ... // getters and setters etc; 
} 

...因为它已经消除了现有的领域。如果这是您需要进行的更改,则它不是新的“版本”,它是一种新类型,并且应该以代码和序列化格式命名。

另一个例子:如果这是您现有的v1类型:

class SomethingFromARequest {   
    Integer requestedId;   
    ... // getters and setters etc;      
}   

...那么这不是该类型的有效“v2”:

class SomethingFromARequest {   
    Long requestedId;   
    ... // getters and setters etc;      
}   

...因为您更改了现有属性的类型。

这些约束更详细地解释为Microsoft's Service Versioning article中与技术无关的方式。


除了避免不兼容的来源之外,您可以而且应该在类型中包含版本号。这可以是一个简单的序列号。如果您习惯于记录或审核消息,并且带宽和存储空间不是问题,则可能需要使用UUID扩充简单整数,以标识类型的每个唯一版本的实例。

此外,您可以通过使用宽松处理和将“额外”数据映射到“额外”字段来设计数据传输对象的向前兼容性。如果XML是您的序列化格式,那么当v1服务收到v2请求(more)时,您可以使用xsd:xmlAny或xsd:any和processContents =“lax”来捕获任何无法识别的架构元素。如果你的序列化格式是JSON,它的内容模型更开放,那么这是免费的。

答案 2 :(得分:3)

我知道现在已经很晚了,但我一直在深入研究这个问题。我真的认为最佳答案涉及另一个难题:服务中介。微软的托管服务引擎就是一个例子 - 我确信其他人也存在。基本上,通过更改Web服务的XML命名空间(包括版本号或日期,如链接文章所提到的),您允许中介将各种客户端调用路由到适当的服务器实现。 MSE的另一个(以及恕我直言,非常酷)功能是执行基于策略的转换的能力。您可以定义将v1请求转换为v2请求的XSLT转换,以及将v2响应转换为v1响应,从而允许您在不破坏客户端实现的情况下淘汰v1服务实现。

答案 3 :(得分:3)

我认为需要注意的是您的客户群,您是公开发布此服务还是仅限于一组已知代理?

我参与了后一种情况,我们发现通过简单的沟通/利益相关方解决这个问题并不困难。

虽然它只与您的问题间接相关,但我们发现基于兼容性的版本编号似乎运行良好。以A.B.C为例......

  • 答:需要重新编译的更改(向后兼容性破坏)
  • B:不需要重新编译的更改,但没有这样做的其他功能(新操作等)
  • C:对不改变WSDL的基础机制的更改