從「SOAP」到「REST」

"SOAP""REST" 程序員

最近有不少同仁問我,咱們爲何要用RESTRESTSOAP好在哪?對於這個問題我想了不下十種答案。但轉念一想,如何以一種最直接,相似於武俠小說中"一劍封喉"般的方式,"穩準狠"的解答他們的問題。就不至於就此展開一場"辯論賽"或是"科普貼"。好在問我這個問題的同仁大都不是程序猿界的"小鮮肉",你們對於時下軟件研發的基本理念仍是有共同認知的,基於此,我對這個問題的標準答案就是:"RESTOO"。架構

若是您還有興趣深刻了解RESTSOAP的區別,我以爲仍是自行Google吧。spa

這篇文章想給你們介紹的重點更偏重於:咱們如何從"SOAP"轉向"REST",以及什麼樣的"RESTful API"纔是真正的REST設計

雖不贅述SOAPREST的概念,但仍是簡單介紹一下何爲SOAP,何爲RESTcode

SOAP(Simple Object Access Protocol):簡單對象訪問協議對象

REST(Rerepresentational State Transfer):表示性狀態轉移接口

你在網上能搜到的異同點大體分爲主要三個方面:進程

  • SOAP是一種協議,而REST是一種規範
  • SOAP支持多種傳輸協議,而REST目前僅支持HTTP/HTTPS
  • SOAP僅支持XML,而REST支持XMLJson甚至HTML

    其實SOAPREST嚴格來講不是兩個對等的概念,咱們姑且理解成兩種服務設計思想及其具體的實現架構。資源

    若是說SOAPREST不夠具象,那我來找兩位代言人,自行感覺一下咱們所謂的"SOAP風格"和"REST風格"。it

    Web Service或是WCF你們想必不陌生,而Web ServiceWCF就是利用SOAP協議進行的服務實現(固然他們也支持REST,但很雞肋,不足以代言)。咱們要對外提供服務離不開相似的技術。那咱們是如何設計這樣的服務接口的呢。提及來真是輕車熟路:咱們先定義對象,然後咱們設計接口。更有甚者,咱們直接定義服務接口。因此一說到Web Service或是WCF咱們就會想到接口咱們一看到接口,咱們就很是的舒服。那咱們爲何舒服,由於這很後臺。進程間的接口調用方式老是會給程序員們以莫名的親切感。

    熟悉Web API風格的朋友可能就能想到REST。可Web API的服務應該怎麼設計?若是咱們還用上面說到設計Web Service/WCF的"SOAP風格"進行服務的設計,那RESTSOAP還有什麼優點?僅僅是更輕量級的優點?

      咱們來舉個例子:一隻喵兒餓了,他要獲取食物,食物分別有魚和熊掌。因此咱們要提供一個服務是食物的獲取。基於這個簡單的小例子,咱們來看看從"SOAP"到"REST"的轉型之路

    第一階段:

    咱們提供一個爲喵兒提供食物的REST服務。接到任務咱們開始進行業務分析,最後肯定2個方法:吃魚和吃熊掌,對!就是這樣一個簡單粗暴的方法,咱們想讓喵兒調用這些方法,調用EatingFish能夠得到魚,調用EatingBearpaw能夠得到熊掌。

    咱們的思路是:定義對象 --定義方法 改寫成RESTful風格

    很快RESTful API 被定義好了:Get  http://service-root/EatingFishGet  http://service-root/EatingBearpaw

    喵兒只要用GET請求這些URI就能獲得食物。

    然而這一版設計很快宣告失敗。並非咱們對服務的功能定義出了問題,而是問題出在這個服務"很不RESTful",緣由是:沒有站在資源的角度考慮RESTful API的定義,而是延續了"接口"定義的"SOAP風格"。這樣從"接口"入手,而後將"接口"以RESTURI形式暴露出來的API仍然是很"SOAP"的,這樣定義出的RESTful API,通常是Level 0Level 1的。

    那咱們就來介紹一下REST4個境界,我也稱之爲REST的"成熟度模型"。

    Level 0:沒有明確的資源概念,只有一個URL,只是用單個HTTP方法

    Level 1:有明確的資源概念,存在不少URL,只是用單個HTTP方法

    Level 2:有明確的資源概念,存在不少URL,使用HTTP做爲資源的統一接口

    Level 3:在知足2級標準的基礎上,使用超媒體做爲應用狀態的引擎

    咱們再來回過頭看看咱們的表達Get  http://service-root/EatingFish,無疑是Level 0

    即使是咱們生硬的在"EatingFish"前將"Food"表達出來也不過是Level1的表達:Get  http://service-root/Food/EatingFish

    分別參照一下Level 0Level 1的定義,咱們發現Level0的表達沒有"資源"的概念,仍然是提供了一個"行爲"的"接口",這種作法像極了"SOAP"。咱們再來看看Level 1的表達,雖然已經明確出了"Food"這個資源,Food資源下也能夠定義多個針對Food的方法,但仍然定義的是諸如"EatingFish"和"EatingBearPaw"這樣的一個個行爲來做爲資源的方法。

              

    第二階段:

             咱們從新思考,要站在"資源"的角度考慮這個服務,而資源的CRUD又能夠經過HTTPPOSTGETPUTDELETE請求表達。也就是說咱們只要把"資源"表述清楚,利用REST的理念CRUD咱們就沒必要考慮了。基於這樣的考慮,咱們趕快調整思路:定義資源 --表述資源

             資源定義:

    咱們定義了三個資源"Food"做爲食物的集合,而"Food"下,有"Fish"和"BearPaw",對於資源的表述咱們採起了Collection+Json的超媒體格式。

     資源表述:

    {

           "collection": {

       "version": "1.0",

       "href": "http://service-root/Food",

       "links": [ ],

       "items": [

         {

            "href": "/Fish",

           "data": [

             {

               "name": "Name",

               "value": ""

             },

             {

               "name": "Code",

               "value": "Fish"

             }

           ],

           "links": [ ]

         },

         {

           "href": "/ BearPaw ",

           "data": [

             {

               "name": "Name",

               "value": "熊掌"

             },

             {

               "name": "Code",

               "value": " BearPaw "

             }

           ],

           "links": [ ]

         }

       ],

    "queries":[ ],

    "template":{

          "data": [

            {

              "name": "Name",

              "value": ""

            },

            {

              "name": "Code",

              "value": ""

            }

          ]

        },

       "error": {

         "code": "",

         "Message": ""

        }

      }

    }

             根據資源的定義,經過Collection+Json這種超媒體類型進行資源的表述。使用HTTP協議語義規定的請求類型做爲資源的統一接口,不須要再像Level 0 Level 1中那樣單獨定義或描述接口。

             這時喵兒若是想要吃魚,只須要GET  http://service-root/Food/Fish他就能得到魚。而若是咱們想要從食物中把熊掌刪除掉,也只須要 DELETE  http://service-root/Food/BearPaw。若是咱們想幫喵增長一種食物-肉,只須要POST http://service-root/Food/Meat,同時利用資源表述中的模板template,將肉的信息傳回去,肉這種食物就被添加進了食物中。

             這時咱們發現咱們再去思考服務的設計已經不是站在"要提供什麼樣的方法"這種基於過程、基於行爲的角度去思考。而是基於"資源"的面向對象的設計方法。

             作到這個程度,感受已經很是的RESTful了,但回到REST的本意看看,就體會出了問題的所在。回觀REST的定義:REST(Rerepresentational State Transfer)表示性狀態轉移,多讀幾遍咱們就漸漸的感受到了一個詞:"轉移"。

    而再對照"成熟度模型"咱們發現這時候的API已是Level 2的。Level 2Level 3,如何表示"轉移"成了下一個階段進階的關鍵。

       

    爲了更好的展示出第三個階段的特色,咱們如今擴充一下這個小例子,在喵兒獲取過食物以後,他還想來點甜點,甜點有蛋糕和冰激凌。(真是隻貪得無厭的喵兒)

       

    第三階段:

    繼續調整思路:定義資源  --定義資源的連接關係 --> 資源表述

    第一步和第三步與第二階段沒有什麼差異,而關鍵在於第二步,REST的本意但願可以經過資源的表述,描述出每一個資源與其餘資源的連接關係。

    回到咱們的例子,咱們定義資源,這時候會有兩棵樹,"食物"和"甜點":

    而接下來,咱們但願喵兒在得到到食物以後,可以知道接下來有甜點吃。這時咱們引入了資源的狀態圖:

    資源的狀態圖表述的是從當下的資源可以連接到哪一個資源。咱們能夠看到當獲取到Fish以後,喵兒就能看到有Desserts,當他訪問Desserts時,就能看到爲他準備好的CakeIce Cream了。

             那這種連接如何表述呢?

             咱們觀察到不論是REST的哪種超媒體格式,都爲咱們準備了Link字段:

    {

           "collection": {

       "version": "1.0",

       "href": "http://service-root/Food ",

       "links": [ ],

       "items": [

          {

            "href":"/Fish",

            "data": [

              {

                "name":"Name",

                "value":""

              },

              {

                "name":"Code",

                "value":"Fish"

              }

            ],

            "links": [

              {

                "rel":"Desserts",

                "href":" http://service-root/Desserts",

               "prompt": "甜點"

              }

            ]

          },

          {

            "href":"/ BearPaw ",

            "data": [

              {

                "name":"Name",

                "value":"熊掌"

              },

              {

                "name":"Code",

                "value":" BearPaw "

              }

            ],

            "links":[

              {

                "rel":"Desserts",

                "href":" http://service-root/Desserts",

               "prompt": "甜點"

              }

            ]       

    }

       ],

       "queries": [ ],

       "error": {

         "code": "",

         "Message": ""

        }

      }

    }

    Level 2 的基礎上,利用Collection+Json中對Links的表達,表述了資源間轉移的關係。若有必要同時利用Queries表達了對資源集合的過濾。至此咱們完成了從"SOAP"到"REST"的轉變。

       

    一些注意:

    1  定義資源不是在定義邏輯模型,更不是E-R

    2  一個服務中描述的資源不必定是同一根的,"轉移"也能夠發生在兩棵樹之間。

    3  一旦出現了不用Level0 Level 1就表達不了的狀況,基本上就是資源的定義出了問題

    4  REST並非"銀彈",它解決不了你資源(對象)設計自己的問題。

相關文章
相關標籤/搜索