构建配置驱动的SOAP/REST集成层:单一服务,多种协议
如果您在企业集成领域工作过,就会熟悉这种模式:您的平台需要与数十个(有时数百个)外部合作伙伴系统通信,而它们对通信方式各执一词。有些期望 SOAP 信封,有些已转向 REST/JSON;有些要求 Basic Auth,有些要求 OAuth,还有些使用私有令牌方案。再加上数据格式的细微差异——不同的 XML 模式、不同的字段名称、不同的嵌套方式——这便是一个典型的集成难题。
2019 年,我接手了一个正好处于这种困境的服务。它是一个基于 .NET 的 SOAP Web 服务,充当中间件层:调用者访问我们的服务,我们的服务再联系客户的 Web 服务,获取数据并将响应返回给原始调用者。请求和响应载荷通过 XSLT 进行转换,每个客户都有对应的专用转换映射到其预期的模式。这种做法在客户端也采用 SOAP 时是可行的。但问题是,越来越多的客户不再使用 SOAP。新客户携带着仅支持 REST/JSON 的 API 出现,而现有架构无法与之通信,除非分叉服务或从头构建一个平行的服务。当您需要为庞大且不断增长的客户群维护集成时,这两种选择都无法很好地扩展。
目标:一个服务,协议无关
目标不是复制服务或维护两套代码库,而是让现有服务变得与协议无关——能够从一个可部署单元中同时与 SOAP 客户和 REST 客户通信,并且协议决策是动态做出的,而非按环境或构建硬编码。最后一点很重要。这不是简单地让 REST 和 SOAP 版本并存。这是一个服务,其与任何下游客户通信时使用的协议由与该客户关联的数据库配置记录决定。新增一个客户,切换一个配置标志,服务就知道如何联系他们——无需重新部署,无需分支代码。
高层流程
- 原始调用者以 XML/SOAP 格式向服务发送请求。
- 服务在数据库中查找目标客户的配置。
- 根据该配置:
- SOAP 路径:请求通过 XSLT 转换为客户期望的 SOAP/XML 模式,并按原样发送。
- REST 路径:请求 XML 通过 XSLT 转换,然后序列化为 JSON,并以 REST 调用形式发送。
- 客户的响应以其使用的格式(XML 或 JSON)返回。
- 如果是 REST/JSON:响应被反序列化并转换回 XML。
- 最终的 XML 响应(无论底层使用何种协议,均已归一化)再次通过 XSLT 转换,然后返回给原始调用者。
关键设计原则:原始调用者永远不需要知道或关心下游客户使用什么协议。 从他们的角度看,他们发送 XML 并收到 XML。所有协议和格式协商都在服务内部完成,完全由配置驱动。
为什么 XSLT 仍是核心
将 XSLT 保留为现已能处理 JSON 的服务的主干可能看起来奇怪,但有一个很好的理由:XSLT 已经在为 SOAP 路径处理每个客户的模式映射,并且当添加 REST 支持时,这些逻辑不需要丢弃——只需要扩展。
对于 REST 客户,管道变为:
返回时:
这意味着在客户特定 XSLT 映射上的大量投资得以延续。无需为每个 REST 客户编写全新转换逻辑,而是复用了相同的模式映射方法,只需在两端加上 JSON 转换步骤。这也意味着,如果客户从 SOAP 迁移到 REST(这种情况发生过不止一次),映射逻辑无需从头重新设计——只有传输和序列化层发生了变化。
设计认证层
客户之间不仅协议不同,认证方式也不同。有些客户仍在使用 Basic Authentication,有些需要 OAuth 令牌流,少数客户拥有不属于任何标准类别的私有令牌方案。为了避免硬编码每个客户的认证逻辑(这样会重蹈协议切换想要解决的维护问题),认证层被设计为可插拔组件,与协议一样通过配置选择:
- Basic 认证:凭据安全存储,并按客户配置附加到出站请求。
- OAuth:令牌获取和刷新在出站调用前透明处理,令牌按需缓存和续期。
- 令牌认证:支持客户颁发的、不遵循标准 OAuth 流的令牌。
认证层被设计为与协议层正交。客户的认证方案和传输协议是独立的配置维度——SOAP 客户可以使用 OAuth,REST 客户可以使用 Basic Auth,等等,任何组合都可以。这种关注点分离后来证明很重要:当客户更新其基础设施时,协议和认证要求很少同步变化,因此保持它们解耦避免了大量“改了一个东西,却要改三个东西”的维护痛苦。
这给我们带来了什么
这种设计带来了一些具体的好处:
- 上手速度:新增一个客户——无论是 SOAP 还是 REST,无论其认证方案如何——都变成了一个配置练习加上一个客户特定的 XSLT 映射,而不是一次新的开发工作。
- 单一代码库,单一部署:没有分叉和维护两个版本的问题。错误修复、性能改进和安全补丁只需应用一次,惠及所有客户。
- 面向未来:随着越来越多的客户逐渐从 SOAP 迁移到 REST(正如预期,这一直在发生),服务不需要架构重构——只需配置更改和新的映射。
- 一致的调用者体验:原始调用者的合同从未改变。内部复杂性完全由服务吸收,外部消费者完全不受影响。
给构建类似中间件的人的建议
如果您也面临类似的集成蔓延问题,我想强调几点:
- 将协议和格式决策推入配置,而非代码。当您为协议处理编写
if (customerX) { ... } else if (customerY) { ... }时,您已经构建了一个在客户数量超过个位数时无法扩展的东西。 - 在添加新协议时,不要丢弃已有的转换逻辑。在本例中,已有的 SOAP 客户 XSLT 投资通过添加序列化步骤无缝扩展到 REST 客户——无需从头重建模式映射。
- 将认证与传输解耦。它们是不同的关注点,客户会以您最初设计可能没有预料到的方式混合搭配方案。
- 朝着事物发展的方向设计。本例中就是 SOAP 到 REST 的迁移。提前构建灵活性,而不是对每个客户的迁移逐一应对,省去了日后大量的一次性工程工作。
最终,这个服务从一个单一协议的 SOAP 集成点演变为,无需重写,就成为了一个持久的、可跨多个产品和客户群复用的基础设施组件——这,事后看来,是衡量集成架构设计好坏的真实标准:不是它能否解决今天的问题,而是它能否在不重写的情况下吸收明天的问题。
DZone 贡献者所表达的观点属于他们个人。