想要设计一个REST Web服务?需要知道REST是什么以及如何去做?传统Web服务和REST之间有什么区别?本文从基础开始,假设您对REST一无所知,并向您展示如何成为REST开发人员和设计师。
##介绍REST代表表述性状态传递。雅虎或谷歌搜索“REST网络服务”会带来大量信息。但是,如果您像我几周前所做的那样 - 您可能会发现所有这些信息比有用的更令人讨厌,因为您希望快速创建REST Web服务。我们自觉或不自觉地都习惯于互联网上的说明指导 - 而且最好能一步一步地指导我们去做点什么。
很快我发现编写一个REST Web服务没有捷径 - 你必须首先完全理解REST是什么。我有一个很好的比喻 - 想想一个C ++或JAVA或.NET新手试图找到关于“如何做面向对象设计”的文章。好吧,祝你好运!REST就是这样 - 专家把它作为一种“架构风格” - 但我用我自己的话写这篇文章,所以我会避开让我困惑的东西。我认为REST是架构和设计合并的地方。
让我们直接开展业务。我将让一个对REST不了解的传统Web服务开发人员开始了解。我会一直保持调理性。##从传统的Web服务转换到REST设计
想想“传统的网络服务”是什么。这是一个暴露“方法”的接口。客户端知道方法的名称,输入和输出,因此可以调用他们。
现在想象一个不暴露“方法”的接口。相反,它暴露“对象”。所以当客户端看到这个接口时,它看到的只是一个或多个“对象”。 “一个对象”没有输入和输出 - 因为“它什么都不做”。它是一个名词,而不是动词。这是“一件事”,而不是“一个行动”。
例如,想想一个传统的Web服务,如果您提供城市,它就会提供当前的天气状况。它可能有一个像GetWeatherInfo()这样的Web方法,它将城市作为输入并提供天气数据作为输出。到目前为止,您很容易理解客户端将如何使用此Web服务。
现在想象一下,在上述网络服务的地方,有一种将城市暴露为对象的新服务。所以,当你把它看作客户端而不是GetWeatherInfo()时,你会看到纽约,达拉斯,洛杉矶和伦敦等等。而这些城市没有任何特殊的应用方法 - 它们显然就像惰性气体 - 它们本身并没有反应。你一定在想 - 呃,作为一个客户端,如何帮助你达到达拉斯的天气?我们会在短时间内达成这一目标。如果你从网络服务中获得的只是一组“对象”,显然你需要一种“按照它们行事”的方式。对象本身没有方法供您调用,因此您需要一组可用于这些对象的操作。换句话说,你需要“将动词应用于名词”。如果你看到一个物体,比如说一个苹果,它是“一个名词”,你可以将“动词”应用到它上面。但并非所有动词都可以应用于所有名词。就像,你可以驾驶汽车,但不能驾驶电视机。因此,如果Web服务仅暴露对象,并且您被问及 - 现在让我们设计一些标准动作或动词,以表示“所有客户端都可以应用到他们看到的。所有对象”,那么您会选择什么动词?你会选择drive?不是。因为这个动作不能应用于大多数暴露的对象。什么是可以应用于所有名词
的动词?我知道你的想法,但是首先我想到了GET!
是的,你可以'得到'一个苹果,'获得'一辆车,甚至'获得'一个城市!从编程的角度来看,“获得”意味着“检索关于”的信息。GET有一个相反的 - PUT。 “放”意味着“更新关于”的信息。等一下...我们听起来和HTTP差不多?当你在你的浏览器上输入一个URL时,下面发生的一系列操作非常接近我刚刚描述的内容!所以你的浏览器应该显示你输入的URL的对应的内容,但是这些内容从哪里得到的?如果您将该URL视为“对象”,现在浏览器需要从远程服务器获取“该对象上的信息”。因此,它发送一个HTTP GET请求。
HTTP是一个“协议”。它说:一个客户端(在这个例子中,浏览器)可以发送请求到一个服务器,它可以是一个GET或一个PUT或一个POST或DELETE(还有其他动词--HTTP 1.1支持8个动词,但让我们现在关注这4个)。所以,如果你暂停一会儿,并认为整个被称为互联网的蜘蛛网实际上是基于这些动词工作的,突然间你会意识到这些动词有多强大 - 突然你对一系列通用行为的搜索将会结束。答案就在你眼前:HTTP动词。事实上,通过使用这些动词之一来完成您在网络上做的任何事情 - 这足以证明这些动词应该是“详尽无遗的”。这
意味着如果一个只公开对象的Web服务支持这四种操作,“在其暴露的对象上”,任何客户端都应该能够对该Web服务执行任何操作!恭喜,您已转换到REST网络服务! REST Web服务是仅暴露对象的服务,并支持这些对象的一组动词。因此,回到我们的天气示例,暴露城市的Web服务也会对这些城市支持GET操作。所以当一个客户发送一个请求,如“GET LOS ANGELES”时,回复可能是洛杉矶的天气数据。答案也可以是关于洛杉矶的任何其他信息 - 例如其人口或一组图像或其历史。但是,您的Web服务可能不会完成所有这些工作。
因此,为您的网络服务选择一个好的域名 - 比如weatherinfo。因此,一个客户端像这样发起请求:
GET http://weatherinfo.com/45327 HTTP/1.1
其中45327是洛杉矶的城市ID,是对您的Web服务的有效请求。您的网络服务可能会回应:
1 HTTP/1.1 200 Ok
2 Date: Sat, 03 Nov 2007 07:35:58 GMT
3 Content-Type: text/xml
4 Content-length: 139
5
6 <City name="Los Angeles" datetime="2007-11-03 07:35:58 GMT" >
7 <Condition>Overcast</Condition>
8 <Temp>69.5</Temp>
9 <Humidity>80</Humidity>
10 </City>
第一行是初始行,第二行到第四行是HTTP头部信息(可以有很多头部信息,这里只显示3个),第5行是分隔标题和正文的强制空行,第6行到第10行构成了“HTTP正文(或内容)“ - 这部分是响应携带的数据,可以是任何格式,不一定是XML。实际上,Web上最常用的格式是HTML-- Web服务器用来将数据发送回浏览器的格式。无论如何,“Content-type”标题通常会指定它。但是如果你正在编写一个Web服务,XML是一个更好的选择,但那只是对应我来说。如果您的Web服务没有返回复杂或复合数据,则格式不需要是XML格式 - 它可以是文本/纯文本,在这种情况下,主体将只是一串字符。
以上实例是您的第一个 REST web 服务的输入和输出!##REST不只是关于Web服务
HTTP创建者有着相同的目标 - 一种通用且简单的客户端服务器通信协议。因此,我们并没有真正重新发明轮子 - 编写REST“Web服务”与编写“Web服务器应用程序”无异。这不就是REST应该的样子吗?因为它也都是web。但是也存在显着差异 - 不是技术方面,而是使用方面。让我们暂时处理这些差异。
首先,当浏览器(思考客户端)发出HTTP请求时:
GET http://www.yahoo.com HTTP/1.0
无论您在雅虎主页上看到什么,浏览器都会收到“大量数据” - 文本,标题,图片和链接。所有数据以“HTML格式”返回到浏览器(作为HTTP正文)。但是,当客户调用“Web服务”来获取某些信息时,通常是一些特定的信息。因此,Web应用程序和Web服务之间的第一个区别在于响应GET的HTTP正文或内容在内容和格式上有很大不同。
其次,浏览器几乎不需要发布除GET之外的任何命令,因此大多数Web应用程序都不会被编码为对PUT或DELETE做出反应。然而,POST由许多服务器处理,但这主要是因为POST是最被滥用的HTTP动词 - 通常,Web应用程序要求客户端发送POST请求,不是因为POST是正确的,而是因为POST可以是灵活地用于任何带来一些传入数据并收回一些传出数据的通用请求。但是,正确设计的REST Web服务应该处理GET,POST,PUT,DELETE和HEAD操作。我们将在本系列的第二部分“设计REST Web服务的步骤”一节中详细介绍这些操作。
本节的要点是:“网络服务仅在使用中与网站不同。”其他一切都是一样的 - 实际上,请求/响应协议也可以是相同的。 HTTP动词足以供任何Web服务使用,因为它们已成功建模所有在线通信。那么,为什么我们要写一个Web服务来创建我们自己的动作或动词呢?创建一个像GetWeatherInfo()这样的web方法不过是创建你自己的动词。可以理解,这种风格是我们桌面编程风格的自然延伸 - 类和接口都有方法,所以Web服务也必须有方法。那么,为什么我们称之为网络服务?这些不仅仅是挑战长期以来成功的传统做事方式的问题。这些问题实际上帮助我们理解了为什么发明SOAP。你知道为什么发明SOAP吗? SOAP是为了结合这些不同风格而发明的--Web风格和服务风格(其中服务=接口或类)。 Web请求使用HTTP协议,但不知何故它必须传达所需操作的名称 - GetWeatherInfo。不仅如此,它还必须携带参数,并执行返回数据。所有这一切都是通过称为SOAP的复杂调制来实现的。但是,我们不得不发明那种调制,只是因为我们懒得放弃传统的“方法调用思维方式”。
#网络思维的两大挑战
您可能会问 - 使用简单的HTTP操作替换传统的Web方法调用实现一切可能并不容易,你是对的 - 这不容易。但花费的努力非常值得。让我们来看看两大挑战。
1 - 用一些简单的动词有效地取代所有的操作需要大胆2 - 像请求/响应机制这样的网络意味着“你必须拥抱无状态”
让我们一步一步来。我们了解了如何取代GetWeatherInfo。但我们将如何取代GetLoanApprovalDecision?或者在电子商务中,我们将如何取代Buy?正如你将要学习的那样,设计一个REST Web服务基本上由两个步骤组成:决定你要公开什么对象,然后决定你如何对每个对象的GET,PUT,POST和DELETE做出反应?那些是你在武器库中拥有的武器。它们以任何方式受到限制或限制吗?在某种程度上,它们是。因为你不能设计一个Web方法,比如GetLoanApprovalDecision。但是这个限制是很好的 - 它迫使我们所有人以标准的方式设计Web服务,强化一些简单而强大的规则,这些规则绝不是不适当的。再次,它就像利用面向对象的方法论的力量 - 给定一个长而复杂的C或VB6程序,你可以将它转换为面向对象的设计,并仍然做同样的事情吗?当然可以。如果你不能,你需要再次回到教室;你不能说OOD不适合这种情况。在我看来,REST设计就是这样 - 如果你不可能想到一种以类似REST的方式为GetLoanApprovalDecision建模的方法,那么去买一本REST的书。 REST可以做到这一点,但你还没有准备好想出来。
我关于这个主题的下一篇文章(第2部分)将从“设计REST Web服务的步骤”开始 - 在这篇文章中,我将尝试探讨我们需要能够做到的思维过程。
转向第二个挑战,无状态:无状态的本质是任何调用都不需要引用任何先前的调用。所有调用都是独立的。服务器不需要了解在处理特定调用时先前调用期间发生的事情。你想要一个例子吗?怎么样的一个Web服务,客户端必须先调用Login()然后调用剩下的东西?这是无状态的吗?不是,因为在处理第二个调用时,Web服务必须记住该用户已经登录了。“登录状态”保存在Web服务中。第二次或第三次调用时,客户端不需要再次传递凭证。这不是无状态,而是“有状态”。 REST不允许这样做,因为真正的可扩展和可扩展的Web服务(或者对于这个问题,网站)不应该是有状态的 - 它应该是无状态的。状态让事情变得复杂,以至于不值得在它上面花时间。允许状态在调用之间蠕变不允许您处理不同计算机上的后续客户端请求。我不会详细讨论有状态的利弊 - 这篇文章不是关于这个。但关键是我们经常设计我们的传统Web服务,并且内置状态。当我们转向REST时,我们不能再这么做了,因为Roy Fielding已经明确禁止REST中的状态。谁是罗伊·菲尔丁?他是一个名字在HTTP规范委员会页面上的人。他是第一次使用“表述性状态传递”这个术语的人。
在本系列的下一部分中,我将介绍设计REST Web服务的具体细节,并着重介绍其实现。我们将看到为什么REST(参数为“REST”),如何构建REST服务和REST客户端。
License
本文以及任何相关的源代码和文件均根据 The Code Project Open License (CPOL)获得许可,关于作者
Koushik Biswas
Koushik是一位架构师,负责雅虎云组织的开发人员和架构师团队,包括媒体内容服务和存储。他是Jadavpur大学的电子工程师,在他职业生涯的大部分时间里一直是顾问。除了花时间工作和项目,他喜欢打板球,听老歌,看科幻电影,野营和钓鱼,各种食物,海滩和燃气烧烤。
原文链接