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

Web services 您的Web服务版本控制最佳实践是什么?,web-services,architecture,versioning,Web Services,Architecture,Versioning,我们有两个独立的产品需要通过web服务相互通信。 支持API版本化的最佳实践是什么 我从2004年开始就声称没有实际的标准,只有最佳实践。有更好的解决办法吗?如何解决WS版本控制问题 问题描述 系统A 客户 服务 可转让对象 版本1 版本2 请求对象 版本1 版本2 系统B 客户 服务 可转让对象 版本1 版本2 请求对象 版本1 版本2 失败场景 如果第1版的系统a客户端调用第2版的系统B服务,它可能会在以下方面失败: SystemBObject(getYear(),getMonth(),g

我们有两个独立的产品需要通过web服务相互通信。 支持API版本化的最佳实践是什么

我从2004年开始就声称没有实际的标准,只有最佳实践。有更好的解决办法吗?如何解决WS版本控制问题

问题描述 系统A 客户 服务 可转让对象 版本1

版本2

请求对象 版本1

版本2

系统B 客户 服务 可转让对象 版本1

版本2

请求对象 版本1

版本2

失败场景 如果第1版的系统a客户端调用第2版的系统B服务,它可能会在以下方面失败:

  • SystemBObject
    getYear()
    getMonth()
    getDay()
    )上缺少方法
  • 未知类型
    BDate
如果第2版系统a客户端调用第1版系统B服务,它可能会在以下情况下失败:

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

  • systemaoobject
    上键入不匹配或溢出(返回
    Long
    ,但应为
    Integer
如果第2版系统B客户端调用第1版系统a服务,它可能会在以下情况下失败:

  • SystemARequest
    上键入missmatch或overflow(请求
    Long
    而不是
    Integer
  • 如果请求以某种方式通过,则强制转换问题(存根是
    长的
    ,但服务返回一个
    整数
    ,在所有WS实现中都不一定兼容)
可能的解决方案
  • 在升级版本时使用数字:例如,
    systemaoobject1
    SystemBRequest2
    等,但缺少用于匹配源/目标版本的API
  • 在签名中,传递XML而不是对象(糟糕,在XML中传递转义XML,双重序列化,反序列化/解析,解析)
  • 其他:例如Document/literal/WS-I是否有补救措施
    我更喜欢Salesforce.com的版本控制方法。每个版本的Web服务都会获得一个不同的URL,格式如下:

    http://api.salesforce.com/{version}/{serviceName}
    
    因此,您的Web服务URL如下所示:

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

    使用此方法,您可以获得以下好处:

  • 你总是知道你在跟哪个版本说话

  • 保持向后兼容性

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


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

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

    通过遵循这一原则,您可以避免您描述的所有问题

    因此,如果这是版本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;      
    }   
    
    …因为您已更改现有属性的类型

    这些约束在本文中以一种主要与技术无关的方式进行了更详细的解释


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


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

    我知道这已经晚了,但我一直在深入研究这个问题。我真的认为最好的答案是另一个难题:服务中介。微软的托管服务引擎就是一个例子——我相信其他引擎也存在。基本上,通过更改web服务的XML名称空间(如链接文章所述,包括版本号或日期),您允许中介将各种客户端调用路由到适当的服务器实现。MSE的另一个(当然,也是非常酷的)特性是能够执行基于策略的转换。您可以定义将v1请求转换为v2请求和v2响应的XSLT转换
    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);
        }
    }
    
    class SystemBObject{
         String key;
         Integer year;
         Integer month;
         Integer day;
    
         ... // getters and setters etc;
    }
    
    class SystemBObject{
         String key;
         BDate date;
         ... // getters and setters etc;
    }
    
    class BDate{
         Integer year;
         Integer month;
         Integer day;
         ... // getters and setters etc;
    
    }
    
    class SomethingFromBRequest {
         String key;
         ... // getters and setters etc;
    }
    
    class SomethingFromBRequest {
         String key;
         BDate afterDate;
         BDate beforeDate;
         ... // getters and setters etc;
    }
    
    http://api.salesforce.com/{version}/{serviceName}
    
    http://api.salesforce.com/14/Lead
    
    http://api.salesforce.com/15/Lead
    
    class SystemBObject{  // version 1
        String key;  
        Integer year;  
        Integer month;  
        Integer day;  
    
        ... // getters and setters etc;  
    }  
    
    // version 2 - NO NO NO 
    class SystemBObject{ 
        String key; 
        BDate date; 
        ... // getters and setters etc; 
    } 
    
    class SomethingFromARequest {   
        Integer requestedId;   
        ... // getters and setters etc;      
    }   
    
    class SomethingFromARequest {   
        Long requestedId;   
        ... // getters and setters etc;      
    }