天天看點

架構之:REST和RESTful

簡介

近幾年微服務是如火如荼的在發展,而微服務之間的調用和漸漸的從RPC調用轉移到了HTTP調用。于是經常聽到有些同僚說我們提供微服務并且暴露RESTful接口給别的系統,但是什麼是RESTful接口呢?它和REST有什麼關系呢?

别急,本文将會帶你一探究竟。

REST

REST是一種架構。

首先我們要記住的是REST是一種架構方式,并不是一種協定。它隻是告訴我們應該如何去搭建一個可靠的系統。

REST的全稱是REpresentational State Transfer。中文可能不好翻譯,我們暫将其定義為有代表性的狀态轉義。它是分布式系統的一種架構方式。最先是由Roy Fielding在2000年他的博士畢業論文中首先提到的。

REST架構在現在的web應用中非常常見,它并不涉及到具體的編碼,它隻是一種進階比的指導方案,具體的實作還是由你自己決定。

REST和RESTful API

我們剛剛講解了REST,那麼REST和RESTful API有什麼關系呢?

我們知道,API是服務和服務之間,用戶端和服務端之間溝通的橋梁,通過API之間的調用,我們可以從伺服器中擷取到需要的資源資訊。而RESTful API就是符合REST架構的API。

是以不是所有的HTTP協定的API都是RESTful API,它的前提是你的系統是REST架構的。

REST架構的基本原則

那麼什麼樣的系統才能被稱為是REST架構的系統呢?根據Roy Fielding的論文描述,REST架構的系統有6個基本特征。我們一一來說明。

Uniform interface統一的接口

在REST架構中,最為核心的元素就是資源。我們将資源定義為一個個的獨立的URI。一個資源用一個獨立并且唯一的URI來表示。

單個的資源不能太大也不能太小,它表示的是一個獨立的可以操作的機關。這些資源通過通用的擷取方式來進行擷取和操作。比如對資源的CURD可以分别用不同的HTTP method來表示(PUT,POST,GET,DELETE)。

同時需要對資源進行統一的命名,定義統一的link格式和資料格式。

還有一點,根據HATEOAS協定,一個資源還應該包含指向該資源或者相關資源的URI。可以能有些同學現在對這一點還有些疑惑,不過沒關系,後面我們會詳細對HATEOAS進行講解。

Spring也提供了對HATEOAS的支援,我們看一個基本的HATEOAS的請求:

GET http://localhost:8080/greeting

該請求的傳回可以是這樣的:

{
  "content":"Hello, World!",
  "_links":{
    "self":{
      "href":"http://localhost:8080/greeting?name=World"
    }
  }
}

           

大家可以看到上面傳回了一個代表自己URI的資源連結。

Client–server 用戶端和伺服器端獨立

另外的一條規則就是用戶端和伺服器端獨立,用戶端和伺服器端互不影響,他們之間的唯一互動就是API的調用。

對于用戶端來說隻要能夠通過API擷取到對應的資源即可,并不關心伺服器是怎麼實作的。

而對于伺服器端來說,隻需要提供保持不變的API即可,自己内部的實作可以自由決定,也不需要考慮用戶端是如何使用這些API的。

這條規則對于現在的很多前後端分離的架構來說已經使用了。

Stateless無狀态

和HTTP協定一樣,REST架構中各個服務之間的API調用也是無狀态的。無狀态的意思是伺服器并不儲存API調用的曆史記錄,也不存儲任何關于用戶端的資訊。對于伺服器來說,每個請求都是最新的。

是以使用者的狀态資訊是在用戶端進行儲存和維護的,用戶端需要在每個接口帶上可以識别使用者的唯一标記,進而在伺服器端進行認證和識别,進而擷取到對應的資源。

Cacheable可緩存

緩存是提升系統速度的利器,對于REST的資源也是一樣的,在REST中對于可緩存的資源需要标明它是可以被緩存的。

進而對應的調用方可以将這些資源進行緩存,進而提升系統的效率。

Layered system分層系統

現代的系統基本上都是分層的,在REST架構中也是一樣,隻要保證對外提供的資源URI是一緻的,架構并不關心你到底使用的是幾層架構。

Code on demand按需編碼

一般來說,REST架構中各個服務通常是通過JSON或者XML來進行互動的。但是這并不是硬性規定。可以傳回可執行的代碼直接運作。

RESTful API的例子

我們來舉幾個常見的RESTful API的例子,來見識一下這種架構的神奇之處:

請求一個entity:

GET https://services.odata.org/TripPinRESTierService/People

根據ID請求一個entity:

GET https://services.odata.org/TripPinRESTierService/People('russellwhyte')

請求一個entity的某個屬性:

GET https://services.odata.org/TripPinRESTierService/Airports('KSFO')/Name

使用filter進行查詢:

GET https://services.odata.org/TripPinRESTierService/People?$filter=FirstName eq 'Scott'

修改資料:

POST https://services.odata.org/TripPinRESTierService/People
header:
{
    Content-Type: application/json
}
body:
{
    "UserName":"lewisblack",
    "FirstName":"Lewis",
    "LastName":"Black",
    "Emails":[
        "[email protected]"
    ],
    "AddressInfo": [
    {
      "Address": "187 Suffolk Ln.",
      "City": {
        "Name": "Boise",
        "CountryRegion": "United States",
        "Region": "ID"
      }
    }
    ]
}

           

删除資料:

DELETE https://services.odata.org/TripPinRESTierService/People('russellwhyte')

更新資料:

PATCH https://services.odata.org/TripPinRESTierService/People('russellwhyte')
header:
{
    Content-Type: application/json
}
body:
{
    "FirstName": "Mirs",
    "LastName": "King"
}
           

總結

繼續閱讀