日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

InfoQ: 描述RESTful應用程序

 ivy1107 2009-02-07
Roy Fielding最近這樣說道

要抓住這類陳述的要領不是件簡單的事。如果服務器不將它自己的名字空間控制在一個固定的資源層次下,客戶端及更重要的客戶端開發(fā)者將如何知道或發(fā)現資源的URI呢?畢竟,長久以來,分布式客戶端/服務器開發(fā)的一個基本假設就是:為了構建、維護和管理這類應用, 我們需要預先對應用的接口正式描述。Roy Fielding的觀點似乎跟這個假設相沖突。

關于描述RESTful系統(tǒng)的討論并非新鮮事物。這類討論幾乎總會得出類似上述的觀點。例如,看看前年infoQ上關于爭論:REST需要描述語言么?的備忘錄,它總結了當時發(fā)生的部分討論。今天的事態(tài)并沒有什么特別的不同。

針對RESTful應用的正式描述語言,雖然有大量的贊成和反對意見,但像WADL這樣的描述語言只得到了有限的發(fā)展。然而,由于缺乏一種機器能夠解釋的“標準”語言,服務器應用所采取的最常用方法就是記錄所有URI、支持的HTTP方法和表示(representation)的結構(如,對應的XML和JSON格式),這樣客戶端應用開發(fā)者就能依賴這種文檔來編寫代碼。

但是,這種方式跟REST的一些基本原則(如Roy Fielding在上面所說的)有沖突。即便我們無視這一異議,對于那些試圖通過HTTP RESTful構建分布式應用的人來說,基本問題仍然存在。不正式地定義契約,服務器怎么可能得以脫身?沒有契約,我們如何能確定正確實現了客戶端和服務器——不僅正確實現了各自的設計規(guī)范,而且恰當地實現了其他業(yè)務/技術策略?

用HTTP作為應用協議、以RESTful方式構建的分布式應用其實有一個契約,但其性質和種類卻不相同。我們需要知道尋找的目標和位置。如果我們打算提出一種描述語言,那么它就要和Roy Fielding所說的保持一致,它不能是類似WSDL或WADL這樣的東西。在這篇文章中,我的目標是回答如下問題:

  • 為什么還沒有一個針對RESTful應用的標準描述語言?
  • RESTful應用的契約應該是個什么樣子?
  • 我們需要構建哪種軟件,它才能理解和利用這樣的契約?
  • 如果我們決定提出機器可讀的描述,它會是什么樣子?

請讓我從一個示例開始。

示例

任務是寫一個客戶端程序,實現同一位客戶在不同銀行賬戶間的轉賬業(yè)務。

首先讓我描述一下客戶端和服務器之間的所有交互,接著看看這個契約的可能描述。

步驟 0:用戶登錄客戶端。為了保持此次討論的重點,請讓我忽視所有安全方面的內容。

步驟 1:客戶端使用URI:http:///accounts?findby=7t676323a通過用戶ID查找兩個賬戶。這里的7t676323a是向銀行注冊了多個賬戶的某用戶的ID。在響應中,服務器返回兩個賬戶ID,即AZA12093ADK31242,各自的用戶ID和當前余額如下:

200 OK
Content-Type: application/xml;charset=UTF-8
<accounts xmlns="urn:org:bank:accounts">
<account>
<id>AZA12093</id>
<customer-id>7t676323a</customer-id>
<balance currency="USD">993.95</balance>
</account>
<account>
<id>ADK31242</id>
<customer-id>7t676323a</customer-id>
<balance currency="USD">534.62</balance>
</account>
</accounts>

我們假設跟名字空間urn:org:bank:accounts綁定的XML模式描述了示例中用到的XML文檔。

步驟 2:由于客戶端知道兩個賬戶的ID,在必要情況下,它可以向以下URI提交GET請求以獲取每個賬戶的詳細信息:

http:///account/AZA12093
http:///account/ADK31242

就這個示例而言,鑒于客戶端已經擁有發(fā)起賬戶轉賬所需的信息,那么就讓我忽略這些請求。

步驟 3:接著,客戶端通過提交如下POST請求發(fā)起賬戶轉賬:

POST /transfers
Host: 
Content-Type: application/xml;charset=UTF-8
<transfer xmlns="urn:org:bank:accounts">
<from>account:AZA12093</from>
<to>account:ADK31242</to>
<amount currency="USD">100</amount>
<note>RESTing</note>
</transfer>

服務器獲得賬戶的路由代碼(譯注:由美國銀行家協會在美聯儲監(jiān)管和協助下提出的金融機構識別碼,很多金融機構都有一個,主要用于銀行相關的交易,轉賬,清算等的路由確認,由9位[8位內容+1位驗證碼]組成,主要用于美國及北美地區(qū)。),把轉賬提交給執(zhí)行轉帳的后端系統(tǒng),并返回如下內容:

201 Created
Content-Type: application/xml;charset=UTF-8
<transfer xmlns="urn:org:bank:accounts">
<from>account:AZA12093</from>
<to>account:ADK31242</to>
<id>transfer:XTA8763</id>
<amount currency="USD">100</amount>
<note>RESTing</note>
</transfer>

轉帳并沒有結束。轉賬將在幾個工作日后異步發(fā)生(這對于銀行間交易很平常),客戶端可以使用交易ID查詢交易狀態(tài)。

步驟 4:一天后,客戶端提交GET請求來查詢狀態(tài)。

GET /check/XTA8763
Host: 
200 OK
Content-Type: application/xml;charset=UTF-8
<status xmlns="urn:org:bank:accounts">
<code>01</code>
<message xml:lang="en">Pending</message>
</status>

注意,盡管這個實現使用了資源、URI、表示和HTTP的統(tǒng)一接口,但它并非RESTful的。因為我們將在后續(xù)小節(jié)看到,這個示例并沒有利用REST的關鍵約束之一,即“超媒體即應用狀態(tài)引擎”。

在試圖使之RESTful之前,讓我先試著寫一份該示例關聯的可能用戶文檔。

Bank.Org API - URIs

http:///accounts?findby=someparams

向這個URI提交GET請求可查詢銀行賬戶。它將返回一個accounts文檔。詳見XML模式。

http:///account/{account-id}

向這個URI提交GET請求可獲取賬戶細節(jié)。這里的{account-id}是賬戶ID。它將返回一個account文檔。詳見XML模式。

http:///transfers

向這個URI提交POST請求可創(chuàng)建一個賬戶轉賬。在請求體中包含了transfer文檔。如果請求成功,服務器會返回一個transfer文檔。詳見XML模式。

http:///check/{transaction-id}

向這個URI提交GET請求可查詢轉賬狀態(tài)。這里的{transaction-id}是賬戶轉賬的ID。它會返回一個status XML文檔。詳見XML模式。

這種風格的文檔在如今很普遍。它包含了客戶端將一直需要使用的所有URI。它描述了客戶端用每個URI可使用的HTTP方法。它還包含了表示的描述,即示例中的XML文檔。

但是這類文檔有兩個問題。首先,它對任何尋找機器可讀正式描述的人并沒有任何幫助。缺少機器可讀的描述,我們就無法構建能用于測試或以其他方式執(zhí)行契約的通用軟件工具。缺乏這類通用軟件工具,對于那些需要部署這類工具來管理和治理他們軟件的人來說,這實在是一個相當大的障礙。你可能會考慮使用WADL,或者甚至是WSDL 2.0來提供一個機器可讀的等價物。

其次,同時也是更重要的,用這種方式描述服務器接口,不論是像WADL或者WSDL 2.0這樣機器可讀的格式,還是人類可讀的格式,都違反了REST的兩個約束。這兩個約束要求(a)消息是自描述的,(b)超媒體為應用狀態(tài)的引擎。怎樣才能做到這些,并且為什么這樣做很重要呢?

回到約束

REST的關鍵約束是(a)資源標示,(b)通過表示操控資源,(c)自描述的消息,(d)超媒體即應用狀態(tài)引擎。

在使用HTTP的RESTful應用中,消息利用兩種東西實現了自描述,其一,通過使用無狀態(tài)的統(tǒng)一接口;其二,通過使用HTTP報頭(Header),它描述了消息內容,除此之外還包括HTTP實現相關的各協議方面(如內容協商、針對緩沖的條件請求和優(yōu)化并發(fā)等等)。

通過檢查使用的HTTP方法和請求/響應報頭,像代理或緩存這樣的中間實體就能夠破譯哪部分協議(即HTTP)正在被使用以及它們是如何被使用的。這類自描述信息保證了客戶端和服務器之間的交互是可見的(如,對緩存的使用),可靠的(如檢測局部故障并從中恢復)和可伸縮的。

第四個約束,即“超媒體即應用狀態(tài)引擎”,有兩個用途。第一,它不要求協議(即HTTP)是有狀態(tài)的。第二,它使服務器可以演變(如,通過引入新的URI)并保持了客戶端跟服務器間的松耦合。

服務器要是象前一節(jié)那樣提供表示的描述,它就沒有利用HTTP自描述的特性。在HTTP中,客戶端和服務器使用“媒體類型(media type)”,或者是那些我們在請求/響應報頭中看到的Content-Type頭信息來描述消息內容,而不是XML模式。媒體類型類似于對象的類或者XML元素的模式類型(schema type)。

此外,如果服務器把所有URI都向它的客戶端描述,它就無法獨立演變,而且接口會變得脆弱。URI的任何改變都有可能讓現有客戶端無法正常工作。但是,你怎樣才能在對客戶端需要連接的URI一無所知的情況下編寫客戶端呢?

答案就是使用具有已知關系的鏈接。鏈接是一種間接機制,客戶端可以用它來在運行時發(fā)現URI。一個鏈接至少有兩個屬性——URI和關系。URI指向資源或者資源的表示,而關系則描述了鏈接的類型或種類。一個真正的RESTful服務器應用是通過在其表示中包含預定義關系的鏈接來把 URI傳給客戶端。于是,客戶端可以無需預先了解所有URI,而是在運行時從鏈接中抽取出URI。由此,服務器可以自由地改變URI,或者甚至在相同或者其他提供兼容性行為的服務器上引入新URI。

最后,通過告知客戶端隨后要做的事,服務器在表示中返回的鏈接可能是上下文相關的。 換句話說,鏈接以一種運行時工作流的形式動態(tài)地描述了客戶端和服務器之間的契約。

總而言之,對于RESTful應用來說,契約包含三個不同部分:統(tǒng)一接口、表示的媒體類型和資源的上下文相關鏈接。

聽起來有些像童話?為了實際地展示這種契約,我會重寫上面的示例。

重寫示例

步驟 0:同前。

步驟 1:客戶端使用相同的URI——http:///accounts?findby=someparams搜索賬戶。這次,讓服務器返回不同類型的響應。

200 OK
Content-Type: application/vnd..account+xml;charset=UTF-8
<accounts xmlns="urn:org:bank:accounts">
<account>
<id>AZA12093</id>
<link href="http:///account/AZA12093" rel="self"/>
<link rel="http:///rel/transfer edit"
type="application/vnd..transfer+xml"
href="http:///transfers"/>
<link rel="http:///rel/customer"
type="application/vnd..customer+xml"
href="http:///customer/7t676323a"/>
<balance currency="USD">993.95</balance>
</account>
<account>
<id>ADK31242</id>
<link href="http:///account/ADK31242" rel="self"/>
<link rel="http:///rel/transfer"
type="application/vnd..customer+xml"
href="http:///transfers"/>
<link rel="http:///rel/customer"
type="application/vnd..customer+xml"
href="http:///customer/7t676323a"/>
<balance currency="USD">534.62</balance>
</account>
</accounts>

在這個響應中,請注意Content-Type報頭的值,以及包含URI的鏈接(link)。

步驟 2:如果客戶端希望了解每個賬戶的更多內容,它可以從上述響應的“self”關系的鏈接中抽取出賬戶URI,向這些URI提交GET請求。

步驟 3:為了發(fā)起賬戶轉賬,客戶端從上述兩個賬戶中任選一個,并從具備“http:///rel/transfer”和“edit”關系的鏈接中抽取出URI,向之提交一個POST請求。

POST /transfers
Host: 
Content-Type: application/vnd..transfer+xml;charset=UTF-8
<transfer xmlns="urn:org:bank:accounts">
<from>account:AZA12093</from>
<to>account:ADK31242</to>
<amount currency="USD">100</amount>
<note>RESTing</note>
</transfer>

同樣請注意Content-Type報頭的值。

發(fā)起賬戶轉賬之后,服務器返回如下內容:

201 Created
Content-Type: application/vnd..transfer+xml;charset=UTF-8
<transfer xmlns="urn:org:bank:accounts">
<link rel="self"
href="http:///transfer/XTA8763"/>
<link rel="http:///rel/transfer/from"
type="application/vnd..account+xml"
href="http:///account/AZA12093"/>
<link rel="http:///rel/transfer/to"
type="application/vnd..account+xml"
href="http:///account/ADK31242"/>
<link rel="http:///rel/transfer/status"
type="application/vnd..status+xml"
href="http:///check/XTA8763"/>
<id>transfer:XTA8763</id>
<amount currency="USD">100</amount>
<note>RESTing</note>
</transfer>

步驟 4:要想查詢賬戶轉賬的狀態(tài),客戶端可以從關系為“http:///check/XTA8763”的鏈接中抽取URI,并向它提交一個GET請求。

這個實現是RESTful的,因為它使用了包含上下文相關鏈接的表示來封裝交互狀態(tài),即利用了“超媒體即應用狀態(tài)引擎”這條約束。

現在,讓我回顧并強調實現這種新交互集合所需的信息。首先,客戶端需要知道查詢賬戶的URI。接著,它需要知道各種鏈接關系的名字和語義。它還需要知道每個媒體類型的細節(jié)。它可以在運行時動態(tài)算出契約的剩余部分。因而,我們可以提供如下修訂后的文檔。

Bank.Org API

URIs

http:///accounts?findby=someparams

向這個URI提交GET請求可查詢銀行賬戶。你可以傳遞客戶ID或客戶名或客戶的社保號碼,將其作為findby的查詢參數值。這個資源支持application/vnd..accounts+xml媒體類型。

鏈接類型

self

帶有這個關系的鏈接,其URI指向包含該鏈接的資源,如賬戶和轉賬資源。

http:///rel/transfer and edit

帶有這些關系的鏈接,其URI能用于創(chuàng)建新的賬戶轉賬資源。

http:///rel/customer

帶有這個關系的鏈接,其URI能用于獲取一個客戶資源。

http:///rel/transfer/from

帶有這個關系的鏈接,其URI標識轉賬的源賬戶資源。

http:///rel/transfer/to

帶有這個關系的鏈接,其URI標識轉賬的目標賬戶資源。

http:///rel/transfer/status

帶有這個關系的鏈接,其URI能用于獲取狀態(tài)資源。

媒體類型

application/vnd..accounts+xml

這個媒體類型的表示包含了在urn:org:bank:accounts名字空間內聲明的accounts文檔。詳見XML模式。

application/vnd..transfer+xml

這個媒體類型的表示包含了在urn:org:bank:accounts名字空間內聲明的transfer文檔。詳見XML模式。

application/vnd..customer+xml

這個媒體類型的表示包含了在urn:org:bank:customer名字空間內聲明的customer文檔。詳見XML模式。

application/vnd..status+xml

這個媒體類型的表示包含了在urn:org:bank:transfer名字空間內聲明的status文檔。詳見XML模式。

不同類型的描述

我在上節(jié)所采用的描述RESTful應用的方法不僅具有某些有趣的特性,亦有些古怪。

對于那些熟悉WSDL和WADL的人來說,上節(jié)的描述可能看起來有些不合常理。我們在其中并未看到關于每個操作輸入和輸出消息的描述,而看到了媒體類型。但是,鑒于像application/xml這樣通用的媒體類型實在是太通用了,無法幫助我們區(qū)分賬戶資源的表示、客戶資源或者轉賬資源的表示。故而在這個示例中,我使用了自定義的媒體類型。每個媒體類項都以“+xml”結尾,并且按照RFC 3023進行描述,XML處理器(包括XMLHttpRequest)能夠把表示當作XML一樣進行處理。通過查看這樣的媒體類型,客戶端就知道收到的表示是一個賬戶資源,還是一個轉賬資源。更重要的是,它不必對它用來獲取那個表示的URI進行任何結構或者語法方面的假設。

此外,文檔并沒有列出應用正在使用的所有的URI,而僅僅包含了賬戶轉賬客戶端需要發(fā)起交互的一個URI。注意,在不同的示例中,我們或許需要記錄 多個URI。其思想是保證預發(fā)布URI的數量最小。為什么這樣更好?原因在于,它解耦了客戶端和資源的實際URI,客戶端直到運行時才需要知道其余的 URI。

最后,上述文檔沒有包括每個URI上可用的HTTP操作。相反,我假定客戶端會向每個URI都提交一個HTTP OPTIONS以發(fā)現各種可能的操作,接著使用HTTP GET獲取資源的表示,使用HTTP POST在資源集合內創(chuàng)建一個新資源,使用HTTP PUT更新現有資源(或者如果客戶端可以為資源分配URI,就創(chuàng)建一個),使用HTTP DELETE刪除資源。

總而言之,要以RESTful方式描述契約必須:

  • 預發(fā)布一些URI并記錄這些URI相應的資源。盡量保證這個列表的長度最短。這些URI是應用的起點。
  • 記錄所有媒體類型。對于基于XML的表示,如果需要,使用XML模式記錄每個表示的結構。
  • 記錄所有鏈接的關系。
  • 讓客戶端在運行時使用HTTP OPTIONS去發(fā)現某URI支持的HTTP操作。在某些情況下,鏈接關系的類型足以讓客戶端確定服務器是否支持某個操作。

這種描述既不完整,也不是完全機器可讀的。

說它不是完整的,是因為它僅僅包含了契約的靜態(tài)部分,讓服務器在運行時通過鏈接描述可能的工作流。

對于那些已經對REST好處深信不疑并且使用HTTP積極構建RESTful應用的人來說,缺乏完整的機器可讀描述可能無關緊要。

但是對于那些正在使用類RPC方法(使用SOAP、WSDL和WS-*)構建分布式應用以及正在考慮REST的人來說,缺乏完整機器可讀的描述可能就是個障礙了。然而,使用RESTful的機器可讀描述可做的工作量,即使存在的話,其作用也有限。這歸結于如下原因:

  • 契約的動態(tài)天性:正如我在上節(jié)示例中所描述的,表示通過在表示中使用鏈接描述了契約的動態(tài)方面。用表示之外、某些基于XML的機器可讀描述來解釋上下文相關的契約是多此一舉。
  • 媒體類型的靈活性:與基于SOAP的應用不同,RESTful應用不限于使用XML。它們可以使用其他沒有模式語言的媒體類型。

同樣需要注意,為遠程接口描述一個完全機器可讀的描述契約是一種謬論。用WSDL或WADL創(chuàng)建的機器可讀描述僅能描述結構和語法,而不能描述語義。但機器可讀的描述有時能降低我們作為程序員、測試員和管理員需要做的工作量。

要是我們把統(tǒng)一接口和契約的動態(tài)方面擱置一邊,我們可以用機器可讀方式描述契約的剩余部分。以下就有一個示例。注意,在這個描述中,我的意圖只是想幫助那些要監(jiān)測或測試客戶端/服務器端交互的工具和框架,當然不是要模仿WSDL或WADL。

<description xmlns:bank="urn:org:bank:accounts">
<types>
<!-- Include the schema used for all representations -->
<include href="bank-schema.rng"/>
</types>
<!-- List all media types and the corresponding XML types -->
<media-types>
<media-type>
<name>application/vnd..accounts+xml</name>
<representation>bank:account</representation>
</media-type>
<media-type>
<name>application/vnd..transfer+xml</name>
<representation>bank:transfer</representation>
</media-type>
...
</media-types>
<relations>
<relation>
<documentation>This relation ...</documentation>
<name>http:///rel/transfer</name>
</relation>
...
</relations>
<resources>
<resource>
<name>accounts</name>
<media-type-ref>application/vnd..accounts+xml</media-type-ref>
<uri> <!-- This is optional -->
<base>http:///accounts</base>
<params>
<param>
<documentation>Use this parameter to ...</documentation>
<name>findBy</name>
</param>
</params>
</uri>
</resource>
<resource>
<name>transfer</name>
<media-type-ref>application/vnd..transfer+xml</media-type-ref>
</resource>
...
</resources>
</description>

這是我在前節(jié)所描述的契約的機器可讀版本,很明顯,它并不符合任何標準。這個描述并沒有消除對于人類可讀描述的需要,因為我們仍然需要描述應用語義。

讓我強調一下這個描述中的關鍵部分:

  • 模式類型: 由于在這個示例中,我為所有的表示選擇使用XML,因而包含了所使用模式的引用。如果模式無法描述所選的表示格式,這部分將毫無意義。
  • 媒體類型以及所使用的相應XML文檔。
  • 所有鏈接關系的列表。
  • 資源和它們媒體類型的名字。注意,這些名字不是URI。
  • 為應用提供起始點的資源的URI。

這種描述比人類可讀的描述更有用嗎?由于缺乏可解釋這種描述的工具和框架,答案可能是否定的。

這種方法實用嗎?

如果你正在編寫基于機器可讀契約(如WADL文檔)的服務器端代碼和客戶端代碼,編碼流程可能如下:

  • 從描述生成資源類。每個資源類都潛在對應于描述中的URI。
  • 生成跟表示綁定的類。如果表示是基于XML的,我會生成跟多種XML文檔匹配的類。
  • 生成跟多種HTTP操作匹配的客戶端存根(stub)。
  • 開始編碼

這個模型對以RESTful方式描述契約并不適用,步驟會有所不同:

  1. 讀取所有媒體類型描述。如果媒體類型是用XML模式描述的,那么就獲取這個模式,生成能夠解析或者生成XML的類。
  2. 讀取所有鏈接關系的描述。
  3. 手工創(chuàng)建資源類。
  4. 每當客戶端接收到一個表示時,除了從表示中抽取數據之外,還要查看鏈接。如果你找到一個包含已知關系的鏈接,那就抽取hreftype(如果有的話)屬性以備后用。
  5. 在客戶端,在發(fā)送HTTP請求之前,先發(fā)送一個HTTP OPTION請求檢查服務器端是否支持你要執(zhí)行的操作。如果支持,在你的客戶端應用中激活該操作。

我關注的大多數軟件框架都可以處理部分上述步驟(如通用接口或資源類的約定),而且還能生成創(chuàng)建或解析XML的類(這取決于你所選的編程語言)。但 是剩余部分就留給了開發(fā)者。更有甚者,這類框架多數強調服務器端編程,并在假設現有HTTP客戶端庫已經足夠使用的情況下忽略了對客戶端編程的考慮。因 而,在處理上述(4)和(5)項時,可能需要創(chuàng)建自定義代碼。

對于那些想要測試或者增強契約的軟件工具怎么辦?創(chuàng)建這種工具,讓其在運行時讀取上述機器可讀描述以完成如下工作,是可行的。

  • 檢查表示的媒體類型是否是預定義的
  • 檢查表示是否匹配媒體類型的預定義描述
  • 檢查表示中包含的所有鏈接是否有預定義關系和媒體類型,并檢查所包含URI是否符合預定義的URI模式。

我還沒有聽說哪個軟件能以這種方式來完成以上驗證。但是,出現的機會很大。如果你讀到文章的這里,你就會明白那些機會是什么。

結論

我寫這篇文章的一個目的是要闡述這樣的事實:像WSDL和WADL這樣的傳統(tǒng)契約描述并不適合描述RESTful應用。正如我在賬戶轉賬示例中所示 范的,只有部分契約能被靜態(tài)地描述,其余都是動態(tài)并上下文相關的??蛻舳丝梢酝ㄟ^在運行時查看鏈接來遵循契約的動態(tài)部分。你可以出于設計時和測試的目的試 著用某些機器可讀文檔來描述前一部分,但是讓服務器在運行時描述其余部分會大大降低客戶端和服務器之間的耦合。試圖靜態(tài)地描述完整契約無異于會使所有上下 文相關的鏈接在表示之外重復一遍。

相反的,諸如WSDL和WADL這樣的描述語言試圖用上下文無關的方式描述契約,并把用戶文檔留給客戶端開發(fā)者,以便他們能夠學習如何從那些描述中描述的各類消息交互模式合成客戶端應用。在RESTful應用中,服務器在運行時以鏈接形式提供這個信息。

總之,RESTful是有契約的。我們只需要知道如何找到并在哪兒找到該契約,同時謹記該契約是上下文相關的,就行了。

關于作者

Subbu在Yahoo工作。通過他的博客可以了解關于他的更多信息。

查看英文原文:Describing RESTful Applications

    本站是提供個人知識管理的網絡存儲空間,所有內容均由用戶發(fā)布,不代表本站觀點。請注意甄別內容中的聯系方式、誘導購買等信息,謹防詐騙。如發(fā)現有害或侵權內容,請點擊一鍵舉報。
    轉藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多