REST API的五種規則

banq發表於2016-12-08
本文提供了五個真正符合RESTful API含義的標準約束。

1.使用應用程式/ JSON媒體型別
API設計其中一個最常見的屬性是使用的媒體型別應用程式/ JSON,或有時使用應用程式/ XML。 透過使用Jersey(JAX-RS),看起來經常是這樣的:

@GET
@Produces(MediaType.APPLICATION_JSON)
public List<Product> getProducts() {
   ...
}
<p class="indent">

使用通用媒體型別(如JSON)基本上不是RESTful,因為REST要求訊息是自描述的。 自我描述只是意味著資料的語義應該與資料本身一起傳播。

以下兩個選項的不同, 看看你喜歡哪一個:

Object getProducts();

或:

List<Product> getProducts();

第一個只是說它返回一個物件,在這種情況下,你必須知道該物件的實際型別是能夠cast投射和使用它。 第二個明確說明它實際上是一個產品列表。

使用通用的媒體型別,需要讓客戶端掌握了JSON訊息內部是什麼才能知道如何使用它。這被稱為隱性知識,主要問題是脆弱性,因為在別處做出的變化,會影響這裡。

為了抵消脆弱性,最好是所有訊息定義自己的媒體型別,就像程式碼中的所有型別定義自己的類一樣。 這個定義不應該只包括語法,還應該包括如何解釋訊息,如何跟蹤它的連結,如何處理它的形式,如何在螢幕上顯示等等。

2.表示中的ID
通常,從伺服器返回(或傳送到伺服器)的表示中包含數字形式的ID(識別符號),類似於資料庫表中的主鍵。 它看起來像這樣:

{ "products": [
  { "id": 12,
    "type": 103,
    "name": "ACME Router D12"
  },
  { "id": 13,
    "type": 145,
    "name": "5m UTP Cable"
  },
  ...
]}
<p class="indent">


使用Java的一個例子,你喜歡哪個API?

List<Long> getProducts();
或:

List<Product> getProducts();
第一個給你一個識別符號ID列表,你可以使用ID從某處獲得一個真正的產品。 第二個返回顯式物件引用,您可以使用直接它們,並直接對這些產品操作。

使用數字ID不是RESTful,這有兩個不同的原因: 首先,它需要來自客戶端的隱含知識,以便知道在哪裡以及如何使用這些ID來獲取產品。 這又增加了脆性和耦合。 其次,它沒有使用HTTP的本地識別符號(URI)來標識產品資源。

上面的例子應該這樣更加RESTful:

{ "products": [
  { "id": "/product/12",
    "type": "/type/103",
    "name": "ACME Router D12"
  },
  { "id": "/product/13",
    "type": "/product/145",
    "name": "5m UTP Cable"
  },
  ...
]}
<p class="indent">


這雖然不是語法上的大變化,但它消除了對於客戶端隱含地東西,非常明確。

3.文件涉及的路徑和引數
很多時候,如果釋出了有關API的任何文件,它包含了請求或響應中存在的路徑,引數,欄位的列表。 像一些Swagger 或 WADL圍繞這些概念被明確為中心。 從這些工具生成的規範和文件都如下所示:

Path: "/products"
  - GET: Get all products
Path: "/product/{id}"
  - GET: Get the product with given id
  - DELETE: Delete the product
...
<p class="indent">


上述文件說明是參考Swagger的PetStore例子。

這不是原始意義上的RESTful,因為它假定客戶端必須知道所有這些路徑和引數,以及它們的含義。 但是,如果表示使用各種說明連結和詳細列表說明,就像上面案例一樣,這些說明資訊都不與客戶端相關,因為客戶端只是跟隨引導到任何地方。

那麼,規範和文件應該做什麼,描述所有定義的媒體型別,很像Java程式碼的API文件。 一個非常小的示例將如下所示:

Type: "application/vnd.acme.productlist+json"
  - Represents a paged product listing in the following JSON format:
    { "products": [
      { "self": "/product/123",
        "name": "ACME Router"
      },
      ...
      ],
      "nextPage": "/products?page=3",
      "previousPage": "/products?page=1"
    }
  - The "self" link in Products leads to the product listed
  - The global "nextPage" link, if present, leads to the next page
    of the product listing
  - Similarly, the "previousPage" to the previous (if not on first page)
<p class="indent">

它沒有具體提及這種表示可能在何處; 它只是描述了一旦收到它如何處理它。 它也沒有提到如何生成到下一頁或上一頁的路徑或如何附加引數或路徑片段,它只是提供供客戶端使用組合的連結。 這些連結是如何建立的並不重要,事實上,伺服器可以決定更改這些實現細節,恕不另行通知。

4. URI模板
URI模板是帶有佔位符的URI。 在命名替換變數的幫助下,它們可以用於生成特定的URI。 他們看起來像這樣:

/product/{id}
/products?page={pageNumber}
/products?startIndex={startIndex}&endIndex={endIndex}
<p class="indent">


這些通常用於規範或文件中以描述資源位於何處,並且期望客戶端知道這些模板並且可以生成特定資源的完整URI。

這是因為這些資源沒有被引用(連結到)其他資源,所以客戶端需要以某種方式手動猜測資源的URI。 這種方法的問題與以前相同;這使得客戶端容易崩潰,如果一些規則改變或者如果伺服器決定引入不同的資源結構。

解決辦法就是讓這些資源能在某個地方被連結引用。

5. URI中的版本號
很多廠商把自己的API的版本放入URI中:

/api/v1/products
/api/products_v1
/api/products?version=v1
<p class="indent">


從原始的REST風格的角度來看,這些看起來非常可疑,因為URI標識了應該代表一些業務相關概念的資源。這些業務概念可能包括人員,客戶,產品,產品列表,訂單等。如果您問業務人員,“什麼是客戶版本2? 他或她可能會非常奇怪地看著你。

URI的實際優點是不會隨著版本升級而改變,在升級單個服務時具有更多的靈活性,能夠將連結設為書籤而不改變它們等。

可以在表示形式或訊息的格式中指定版本號:
application/vnd.acme.product-v1+json

或者使用媒體型別定義也可以定義額外引數:

application/vnd.acme.product+json; version=1


總結
REST中至少應該有以下兩個含義:

1.複雜整合問題的原始含義,這是透過對架構的嚴格約束解決的。

2.服務中通俗意義,這是透過HTTP釋出某種基於JSON介面的某種形式實現的。

5 Signs That Your REST API Isn't RESTful

相關文章