ヘッダーを使用した条件付き操作
このセクションでは、Spring Data REST がどのように標準の HTTP ヘッダーを使用してパフォーマンスを向上させ、操作を条件付けし、より洗練されたフロントエンドに貢献するかを示します。
ETag
、If-Match
、If-None-Match
ヘッダー
ETag
ヘッダー [IETF] (英語) は、リソースにタグを付ける方法を提供します。これにより、クライアントが相互にオーバーライドするのを防ぐと同時に、不要な呼び出しを減らすことができます。
次の例を考えてみましょう。
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
ヘッダーと一致する場合にのみ、操作が実行されます。このセーフガードは、クライアントがお互いを踏みつけるのを防ぎます。2 つの異なるクライアントがリソースをフェッチし、同一の ETag
を持つことができます。1 つのクライアントがリソースを更新すると、レスポンスで新しい ETag
を取得します。しかし、最初のクライアントにはまだ古いヘッダーがあります。そのクライアントが If-Match
ヘッダーを使用して更新を試みた場合、それらが一致しなくなったため、更新は失敗します。代わりに、そのクライアントは HTTP 412 Precondition Failed
メッセージを受信して送り返します。その後、クライアントは追いつくことができますが、必要です。
「バージョン」という用語は、データストアが異なる場合のセマンティクスや、アプリケーション内のセマンティクスが異なる場合もあります。Spring Data REST は、データストアのメタモデルに効果的に委譲して、フィールドがバージョン管理されているかどうかを識別し、バージョン管理されている場合は、ETag 要素が一致する場合にのみリストされた更新を許可します。 |
If-None-Match
ヘッダー [IETF] (英語) は代替手段を提供します。条件付き更新の代わりに、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
ヘッダー [IETF] (英語) は、最後のリクエスト以降にリソースが更新されたかどうかを確認する方法を提供します。これにより、アプリケーションは同じデータを再送信する必要がなくなります。次の例を考えてみましょう。
@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
次の例に示すように、この値をキャプチャーして後続のクエリに使用すると、更新されていないときに同じデータが 2 回フェッチされるのを防ぐことができます。
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
)に格納します。リソースをフェッチするときは、リソースに簡単に戻れるように、DOM ノード(おそらく
data-uri
またはdata-self
)にもself
URI を埋め込みます。If-Match
を使用し、HTTP412 Precondition Failed
ステータスコードも処理するようにPUT
/PATCH
/DELETE
操作を調整します。If-None-Match
およびIf-Modified-Since
を使用し、HTTP304 Not Modified
ステータスコードを処理するようにGET
操作を調整します。
ETag
要素と Last-Modified
値を DOM(またはネイティブモバイルアプリの場合は他の場所)に埋め込むことで、同じものを何度も取得しないことで、データとバッテリー電力の消費を減らすことができます。また、他のクライアントとの衝突を回避し、代わりに、違いを調整する必要があるときにアラートを受け取ることもできます。
このように、フロントエンドを少し調整し、エンティティレベルで編集するだけで、バックエンドは、顧客フレンドリーなクライアントを構築するときに利用できる時間に敏感な詳細を提供します。