查看: 7582|回复: 4

REST 及其版本控制

[复制链接]
论坛徽章:
63
2010广州亚运会纪念徽章:台球
日期:2010-10-18 12:43:48茶鸡蛋
日期:2013-01-09 10:59:002013年新春福章
日期:2013-02-25 14:51:24奥运会纪念徽章:帆船
日期:2013-04-02 17:07:052013年新春福章
日期:2013-04-08 17:42:48奥运纪念徽章
日期:2013-07-18 13:55:12优秀写手
日期:2013-12-18 09:29:10马上有车
日期:2014-03-20 16:13:24马上有房
日期:2014-03-20 16:14:11马上有钱
日期:2014-03-20 16:14:11
跳转到指定楼层
1#
发表于 2010-6-18 21:40 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
与REST相关的最棘手的问题之一:基于REST架构的服务版本控制。Ganesh通过一个示例说明:

    假设有一个资源,它的标识是这样的,“/customers/1234”。人们通过PUT修改这个资源的状态。[但是]REST 如何处理PUT对应的业务逻辑的变更呢?

因为无法通过修改操作名(如,指定PUTv2)的方式标识服务的业务逻辑发生的变化,一种可能的做法是修改PUT使用的URL,与此同时维护原有的资源标识,Ganesh描述到:

    如PUT /customers/v2/1234,这不同于客户端的原有标识/customers/1234。

该方法类似于Peter Williams在几年前谈到,而且其他人甚至在更早就谈到的反模式。Peter在2008年如是说:

    我非常痛恨这种方法,其隐含的意思是一个版本的API与另一版本的API表征不同的资源。它迫使客户端作出进退两难的选择,不是同时支持多版本的API,就是打破REST的核心限制之一。例如,一个V1版API的客户保存了URL引用(该URL中包括版本标识),一段时间后客户端更新到新版的API。 此时,客户端支持该API的多个版本,因为所有之前保存的URI全部指向旧版本的API,或者必须要将这些URI转接到新版API。URI转接打破了 REST的将多媒体当作应用程序状态引擎(HATEOAS)的限制,而支持多版本的API是维护工作中的噩梦。

Peter建议使用定制媒体类型与(最终必将发生的非向后兼容性变更的)内容协商的结合进行版本控制。

    该方法的确会导致新建媒体类型,但是媒体类型是低成本的,所以我们可以,而且应该,需要多少媒体类型就创建多少。如果能正确使用,内容协商可用于解决REST/HTTP服务接口的版本控制问题。

但是,回到Ganesh以及最初的修改URL的方法,他也不同意这种方法。

    首先,如果认为只有两处(动词以及资源)可以存放业务逻辑的不同版本,那就是一个错误。第三处是所提交的数据中……但此例仅仅引发了我对版本控制根本的探究。

进而他提出了REST与SOA所需的版本控制的问题,它问道,首先版本控制是什么,其次为什么需要它?

    我认为服务版本控制是一种机制,它使我们能够以消费者可见的方式同时维护两组或多组业务逻辑的版本。

首先他问道,为什么要对消费者可见,如果服务能够区别不同的消费者类型,那么它就能在内部为不同的调用者(客户端/消费者)应用不同的业务逻辑?接着它又进一步提出问题:我们为什么要同时维护不同版本的业务逻辑呢?

    一个有趣(又迂回)的答案是,业务逻辑通常需要对消费者可见,所以新版的业务逻辑也需要以消费者可见的方式与其旧版本进行区别。这往往被描述成是对旧消费者的支持,比如,消费者在某方面依赖于旧版本的业务逻辑的服务消费者。

支持旧客户的目的是确保现有的契约和SLA不会因为服务的(悄然)更新而被破坏。这就引出了Ganesh的又一问题,版本控制的问题的解决办法可否不通过使用显式的版本,而在对具体实施细节进行抽象时找到呢?也许服务契约太细节了以至于在变更发生时显得脆弱。(类似于过去SOA背后的一个重要原则,松耦合与紧耦合的思想)

Ganesh接着通过一个例子描述了如何使用这种方法解决版本控制的问题。在该例中,一个虚构的开发人员在已有服务消费者的情况下悄悄地完成了对服务的业务逻辑的变更。

    第一个问题是,新的业务逻辑能否应用于所有的消费者,或者我们是否需要跟踪“旧”消费者并继续为他们保留旧的业务逻辑?如果我们能够为所有消费者升级到新的业务逻辑,那是当然没有任何问题的。接口继续保持不变,客户端应用程序将数据POST到相同的URI,请求接着被转发到新建资源的地址……所应用的业务逻辑是全新的,但是客户端不会从接口中感知到变化……

但是,当然也有可能根本无法做到这样,这时就需要维护业务逻辑的多个版本。Ganesh接着描述了这种情况发生的几个可能的原因:

    * 客户端应用程序应该提交的数据有变化,这种变更不可避免地要让消费者知情,而且不得不使用新契约进行交互。
    * 另一商业原因是区分两类客户,也许是要奖励长期的客户,为他们提供更好的服务,不过该变化不会从客户端提交的数据中察觉出来。
    * 客户端应用程序可能“了解”旧版本的服务行为并依赖于这些行为,这种情况下,需要使用新的版本已避免d对遗留客户造成破坏。

Ganesh认为,第三种情况是人为的,它意味着通过消费者和服务实现之间的显式依赖关系破坏了服务的松耦合性。

    相反,第一种和第二种情况都有相应的解决方案。如果由客户端提交的数据要发生改变,那么这本身就是一种区别新消费者与旧消费者的方式,从而可以为他们提供不同的业务逻辑。换言之,我们仅需告诉新消费者他们需要对POST的数据作出的修改。旧消费者什么都不用做。此外,如果我们能够在某种程度上得知消费者是旧消费者,那么,即使它既提交的数据没有明显的区别,我们也能透明地应用不同的业务逻辑。

他这样总结自己的观点……

    服务的版本不是接口细节,而往往是在接口中泄漏的实现细节。

服务的版本控制问题是许多领域里的常见话题,不仅仅是REST。然而,Ganesh坚信REST为该问题提供了更好,更自然的解决方法。
论坛徽章:
131
乌索普
日期:2017-09-26 13:06:30马上加薪
日期:2014-11-22 01:34:242014年世界杯参赛球队: 尼日利亚
日期:2014-06-17 15:23:23马上有对象
日期:2014-05-11 19:35:172014年新春福章
日期:2014-04-04 16:16:58马上有对象
日期:2014-03-08 16:50:54马上加薪
日期:2014-02-19 11:55:14马上有对象
日期:2014-02-19 11:55:14马上有钱
日期:2014-02-19 11:55:14马上有房
日期:2014-02-19 11:55:14
2#
发表于 2010-6-19 00:00 | 只看该作者
nice job

使用道具 举报

回复
论坛徽章:
277
马上加薪
日期:2014-02-19 11:55:14马上有对象
日期:2014-02-19 11:55:14马上有钱
日期:2014-02-19 11:55:14马上有房
日期:2014-02-19 11:55:14马上有车
日期:2014-02-19 11:55:14马上有车
日期:2014-02-18 16:41:112014年新春福章
日期:2014-02-18 16:41:11版主9段
日期:2012-11-25 02:21:03ITPUB年度最佳版主
日期:2014-02-19 10:05:27现任管理团队成员
日期:2011-05-07 01:45:08
3#
发表于 2010-6-19 08:45 | 只看该作者
good.

使用道具 举报

回复
论坛徽章:
131
乌索普
日期:2017-09-26 13:06:30马上加薪
日期:2014-11-22 01:34:242014年世界杯参赛球队: 尼日利亚
日期:2014-06-17 15:23:23马上有对象
日期:2014-05-11 19:35:172014年新春福章
日期:2014-04-04 16:16:58马上有对象
日期:2014-03-08 16:50:54马上加薪
日期:2014-02-19 11:55:14马上有对象
日期:2014-02-19 11:55:14马上有钱
日期:2014-02-19 11:55:14马上有房
日期:2014-02-19 11:55:14
4#
发表于 2010-6-21 23:59 | 只看该作者
nice job

使用道具 举报

回复
论坛徽章:
0
5#
发表于 2018-8-8 17:17 | 只看该作者
以web服务的形式调用rest服务 此种方式对rest服务的声明有一定要求,声明方式如下所示,仅供参考: @Mapping("/rest/getUserInfo" public Renderer cnkeet() throws Exception { ActionContext ac=ActionContext.getActionContext(); HttpServletRequesthrq=ac.getHttpServletRequest();Stringid=hrq.getParameter(“id”); JSONObject object=new JSONObject(); object.put(“id”, id); return new PlainRenderer(object.toString()); } 此种声明方式需要通过request获取参数值】 注:如果rest服务声明为如下形式 @Mapping("/rest/getUserInfo/$1/$2" public Renderer getUserInfo(String id) throws Exception { } 工作流在调用rest服务时,url为http://iport/context/rest/getUserInfo?t=""&n="" 。与rest声明不符,调用失败, 导致调用rest服务失败。具体可参考东软官网,https://platform.neusoft.com/

使用道具 举报

回复

您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

TOP技术积分榜 社区积分榜 徽章 团队 统计 知识索引树 积分竞拍 文本模式 帮助
  ITPUB首页 | ITPUB论坛 | 数据库技术 | 企业信息化 | 开发技术 | 微软技术 | 软件工程与项目管理 | IBM技术园地 | 行业纵向讨论 | IT招聘 | IT文档
  ChinaUnix | ChinaUnix博客 | ChinaUnix论坛
CopyRight 1999-2011 itpub.net All Right Reserved. 北京盛拓优讯信息技术有限公司版权所有 联系我们 未成年人举报专区 
京ICP备16024965号-8  北京市公安局海淀分局网监中心备案编号:11010802021510 广播电视节目制作经营许可证:编号(京)字第1149号
  
快速回复 返回顶部 返回列表