帶有標頭的條件式操作
本節說明 Spring Data REST 如何使用標準 HTTP 標頭來增強效能、條件化操作,並為更複雜的前端做出貢獻。
ETag
、If-Match
和 If-None-Match
標頭
ETag
標頭提供了一種標記資源的方法。這可以防止客戶端互相覆寫,同時也可以減少不必要的呼叫。
考慮以下範例
class Sample {
@Version Long version; (1)
Sample(Long version) {
this.version = version;
}
}
1 | @Version 註解(如果您使用 Spring Data JPA,則為 JPA 的註解;對於所有其他模組,則為 Spring Data 的 org.springframework.data.annotation.Version 註解)將此欄位標記為版本標記。 |
當前述範例中的 POJO 由 Spring Data REST 作為 REST 資源提供時,會帶有一個 ETag
標頭,其值為版本欄位的值。
如果我們提供類似以下的 If-Match
標頭,我們可以有條件地 PUT
、PATCH
或 DELETE
該資源
curl -v -X PATCH -H 'If-Match: <value of previous ETag>' ...
只有當資源目前的 ETag
狀態與 If-Match
標頭相符時,才會執行操作。此安全措施可防止客戶端互相覆寫。兩個不同的客戶端可以提取資源並具有相同的 ETag
。如果一個客戶端更新了資源,它會在回應中獲得一個新的 ETag
。但第一個客戶端仍然擁有舊的標頭。如果該客戶端嘗試使用 If-Match
標頭進行更新,則更新會失敗,因為它們不再匹配。相反地,該客戶端會收到 HTTP 412 Precondition Failed
訊息以發送回去。然後,客戶端可以根據需要進行趕上。
術語「版本」可能在不同的資料儲存區中帶有不同的語意,甚至在您的應用程式中也可能帶有不同的語意。Spring Data REST 有效地委派給資料儲存區的中繼模型,以辨別欄位是否已版本化,如果是,則僅在 ETag 元素匹配時才允許列出的更新。 |
If-None-Match
標頭提供了一種替代方案。If-None-Match
不是條件式更新,而是允許條件式查詢。考慮以下範例
curl -v -H 'If-None-Match: <value of previous etag>' ...
上述命令(預設情況下)執行 GET
。Spring Data REST 在執行 GET
時會檢查 If-None-Match
標頭。如果標頭與 ETag 相符,則它會得出結論,認為沒有任何變更,並且不會傳送資源的副本,而是傳回 HTTP 304 Not Modified
狀態碼。從語意上來說,它的意思是「如果此提供的標頭值與伺服器端版本不符,則傳送整個資源。否則,不傳送任何內容。」
此 POJO 來自於基於 ETag 的單元測試,因此它沒有 @Entity (JPA) 或 @Document (MongoDB) 註解,如同在應用程式碼中所預期的。它僅專注於具有 @Version 的欄位如何產生 ETag 標頭。 |
If-Modified-Since
標頭
If-Modified-Since
標頭提供了一種檢查資源自上次請求以來是否已更新的方法,這讓應用程式可以避免重新傳送相同的資料。考慮以下範例
@Document
public class Receipt {
public @Id String id;
public @Version Long version;
public @LastModifiedDate Date date; (1)
public String saleItem;
public BigDecimal amount;
}
1 | Spring Data Commons 的 @LastModifiedDate 註解允許以多種格式(JodaTime 的 DateTime 、舊版 Java Date 和 Calendar 、JDK8 日期/時間類型,以及 long /Long )擷取此資訊。 |
在前述範例中使用日期欄位,Spring Data REST 會傳回類似以下的 Last-Modified
標頭
Last-Modified: Wed, 24 Jun 2015 20:28:15 GMT
此值可以被擷取並用於後續查詢,以避免在資料未更新時重複提取相同的資料,如下列範例所示
curl -H "If-Modified-Since: Wed, 24 Jun 2015 20:28:15 GMT" ...
使用上述命令,您要求僅在資源自指定時間以來已變更時才提取資源。如果是,您將收到修訂後的 Last-Modified
標頭,用於更新客戶端。如果不是,您將收到 HTTP 304 Not Modified
狀態碼。
標頭的格式非常完美,可以傳回以供未來查詢使用。
請勿將標頭值與不同的查詢混合和匹配。結果可能是災難性的。僅當您請求完全相同的 URI 和參數時,才使用標頭值。 |
架構更有效率的前端
ETag
元素與 If-Match
和 If-None-Match
標頭結合使用,可讓您建構對消費者資料方案和行動裝置電池壽命更友善的前端。若要執行此操作
-
識別需要鎖定的實體,並新增版本屬性。
HTML5 很好地支援
data-*
屬性,因此將版本儲存在 DOM 中(例如data-etag
屬性)。 -
識別可從追蹤最新更新中受益的條目。在提取這些資源時,將
Last-Modified
值儲存在 DOM 中(可能是data-last-modified
)。 -
在提取資源時,也將
self
URI 嵌入到您的 DOM 節點中(可能是data-uri
或data-self
),以便輕鬆返回資源。 -
調整
PUT
/PATCH
/DELETE
操作以使用If-Match
,並處理 HTTP412 Precondition Failed
狀態碼。 -
調整
GET
操作以使用If-None-Match
和If-Modified-Since
,並處理 HTTP304 Not Modified
狀態碼。
透過將 ETag
元素和 Last-Modified
值嵌入到您的 DOM 中(或者可能是原生行動應用程式的其他位置),您可以減少資料和電池電量的消耗,而無需重複檢索相同的內容。您也可以避免與其他客戶端衝突,而是在需要協調差異時收到警示。
透過這種方式,只需稍微調整您的前端和一些實體層級的編輯,後端即可提供時間敏感的詳細資訊,您可以在建構客戶友善的客戶端時利用這些資訊。