Conditional Operations with Headers
本节显示 Spring Data REST 如何使用标准 HTTP 头部来增强性能、条件化操作以及促成更为完善的前端。
This section shows how Spring Data REST uses standard HTTP headers to enhance performance, conditionalize operations, and contribute to a more sophisticated frontend.
ETag
, If-Match
, and If-None-Match
Headers
ETag
header 提供了一种标记资源的方法。这可以防止客户端相互覆盖,同时也可能减少不必要的调用。
The ETag
header provides a way to tag resources. This can prevent clients from overriding each other while also making it possible to reduce unnecessary calls.
请考虑以下示例:
Consider the following example:
Unresolved include directive in modules/ROOT/pages/etags-and-other-conditionals.adoc - include::example$support/ETagUnitTests.java[]
1 | @Version`注释(如果你正在使用Spring Data JPA,那么是JPA的,如果是其它所有模块,则是Spring Data `org.springframework.data.annotation.Version )将此字段标记为版本标记。 |
2 | The @Version annotation (the JPA one in case you’re using Spring Data JPA, the Spring Data org.springframework.data.annotation.Version one for all other modules) flags this field as a version marker. |
前面对例中的 POJO 通过 Spring Data REST 作为 REST 资源提供时,具有带有版本字段值的 ETag
标头。
The POJO in the preceding example, when served up as a REST resource by Spring Data REST, has an ETag
header with the value of the version field.
如果我们提供以下 If-Match
标头,我们可以有条件地 PUT
、PATCH
或 DELETE
该资源:
We can conditionally PUT
, PATCH
, or DELETE
that resource if we supply a If-Match
header such as the following:
curl -v -X PATCH -H 'If-Match: <value of previous ETag>' ...
仅当资源的当前 ETag
状态与 If-Match
标头匹配时,才会执行该操作。此保护措施可防止客户相互影响。两个不同的客户可以提取资源并拥有相同的 ETag
。如果一个客户更新资源,它会在响应中获取一个新的 ETag
。但是第一个客户仍然拥有旧标头。如果该客户尝试使用 If-Match
标头进行更新,则更新会失败,因为它们不再匹配。相反,该客户会收到一个 HTTP 412 Precondition Failed
消息以进行发送回。然后,该客户可以按需要进行追赶。
Only if the resource’s current ETag
state matches the If-Match
header is the operation carried out. This safeguard prevents clients from stomping on each other. Two different clients can fetch the resource and have an identical ETag
. If one client updates the resource, it gets a new ETag
in the response. But the first client still has the old header. If that client attempts an update with the If-Match
header, the update fails because they no longer match. Instead, that client receives an HTTP 412 Precondition Failed
message to be sent back. The client can then catch up however is necessary.
术语"`version,`"对于不同的数据存储可能具有不同的语义,甚至可能在你的应用程序中具有不同的语义。Spring Data REST 实际上委托给数据存储的元模型,以识别字段是否具有版本,如果具有,则仅在`ETag`元素匹配时允许列出的更新。
The term, “version,” may carry different semantics with different data stores and even different semantics within your application. Spring Data REST effectively delegates to the data store’s metamodel to discern if a field is versioned and, if so, only allows the listed updates if ETag
elements match.
If-None-Match
header 提供了一个替代。If-None-Match
允许条件查询,而不是条件更新。考虑以下示例:
The If-None-Match
header provides an alternative. Instead of conditional updates, If-None-Match
allows conditional queries. Consider the following example:
curl -v -H 'If-None-Match: <value of previous etag>' ...
前面的命令(默认情况下)运行 GET
。Spring Data REST 会在进行 GET
时检查 If-None-Match
标头。如果标头与 ETag 匹配,它会得出以下结论:没有任何更改,并且返回 HTTP 304 Not Modified
状态代码,而不是发送资源副本。在语义上,它读作“如果提供的标头值与服务器端版本不匹配,则发送整个资源。否则,不发送任何内容。
”
The preceding command (by default) runs a GET
. Spring Data REST checks for If-None-Match
headers while doing a GET
. If the header matches the ETag, it concludes that nothing has changed and, instead of sending a copy of the resource, sends back an HTTP 304 Not Modified
status code. Semantically, it reads “If this supplied header value does not match the server-side version, send the whole resource. Otherwise, do not send anything.”
此 POJO 来自基于`ETag`的单元测试,因此它没有在应用程序代码中预期的`@Entity`(JPA)或`@Document`(MongoDB)注解。它仅关注字段如何通过`@Version`导致`ETag`标头。 |
This POJO is from an |
If-Modified-Since
header
If-Modified-Since
header 提供了一种方法来检查自上次请求以来是否已经更新了资源,这可以让应用程序避免重新发送相同的数据。考虑以下示例:
The If-Modified-Since
header provides a way to check whether a resource has been updated since the last request, which lets applications avoid resending the same data. Consider the following example:
Unresolved include directive in modules/ROOT/pages/etags-and-other-conditionals.adoc - include::example$mongodb/Receipt.java[]
1 | Spring Data Commons的`@LastModifiedDate`注释允许以多种格式捕获此信息(JodaTime的`DateTime`、遗留的Java`Date`和`Calendar`、JDK8日期/时间类型以及`long`/Long )。 |
2 | Spring Data Commons’s @LastModifiedDate annotation allows capturing this information in multiple formats (JodaTime’s DateTime , legacy Java Date and Calendar , JDK8 date/time types, and long /Long ). |
通过前面对例中的日期字段,Spring Data REST 返回类似于以下内容的 Last-Modified
标头:
With the date field in the preceding example, Spring Data REST returns a Last-Modified
header similar to the following:
Last-Modified: Wed, 24 Jun 2015 20:28:15 GMT
此值可以被捕获并用于后续查询,以避免在未更新时两次提取相同数据,如下例所示:
This value can be captured and used for subsequent queries to avoid fetching the same data twice when it has not been updated, as the following example shows:
curl -H "If-Modified-Since: Wed, 24 Jun 2015 20:28:15 GMT" ...
通过前面的命令,您要求仅当资源自指定时间以来已更改时才提取资源。如果是,您将获得经过修改的 Last-Modified
标头,以用于更新客户。如果不是,您会收到一个 HTTP 304 Not Modified
状态代码。
With the preceding command, you are asking that a resource be fetched only if it has changed since the specified time. If so, you get a revised Last-Modified
header with which to update the client. If not, you receive an HTTP 304 Not Modified
status code.
该标头经过完美格式化,可以发送回以进行将来查询。
The header is perfectly formatted to send back for a future query.
不要将标头值与不同的查询混合匹配。结果可能是灾难性的。仅在请求完全相同的 URI 和参数时使用标头值。
Do not mix and match header value with different queries. Results could be disastrous. Use the header values ONLY when you request the exact same URI and parameters.
Architecting a More Efficient Front End
ETag
元素与 If-Match
和 If-None-Match
标头组合使用,使您可以构建一个更友好的前端,以供用户使用其数据计划和移动端电池续航。要做到这一点:
ETag
elements, combined with the If-Match
and If-None-Match
headers, let you build a front end that is more friendly to consumers' data plans and mobile battery lives. To do so:
-
识别需要锁定的实体并添加版本属性。[.iokays-translated-e8c51b811b0f78240f2dcfaaebeb0dd9] HTML5 很好的支持
data-*
属性,因此将版本存储在 DOM 中(例如此处所示的data-etag
属性中)。
HTML5 nicely supports data-*
attributes, so store the version in the DOM (somewhere such as an data-etag
attribute).
-
Identify the entities that need locking and add a version attribute.[.iokays-translated-e8c51b811b0f78240f2dcfaaebeb0dd9] HTML5 很好的支持
data-*
属性,因此将版本存储在 DOM 中(例如此处所示的data-etag
属性中)。
HTML5 nicely supports data-*
attributes, so store the version in the DOM (somewhere such as an data-etag
attribute).
-
识别可以从跟踪最新更新中受益的条目。在获取这些资源时,将`Last-Modified`值存储在DOM中(可能为`data-last-modified`)。
-
Identify the entries that would benefit from tracking the most recent updates. When fetching these resources, store the
Last-Modified
value in the DOM (data-last-modified
perhaps). -
在获取资源时,也可以在你的DOM节点(可能是`data-uri`或`data-self`)内嵌入`self`URI,以便轻松返回资源。
-
When fetching resources, also embed
self
URIs in your DOM nodes (perhapsdata-uri
ordata-self
) so that it is easy to go back to the resource. -
调整`PUT`/
PATCH
/DELETE`操作以使用`If-Match
,并且还处理HTTP `412 Precondition Failed`状态码。 -
Adjust
PUT
/PATCH
/DELETE
operations to useIf-Match
and also handle HTTP412 Precondition Failed
status codes. -
调整`GET`操作以使用`If-None-Match`和`If-Modified-Since`,并且处理HTTP `304 Not Modified`状态码。
-
Adjust
GET
operations to useIf-None-Match
andIf-Modified-Since
and handle HTTP304 Not Modified
status codes.
在你的 DOM (或对于原生移动应用而言可能在其他位置)中嵌入 ETag
元素和 Last-Modified
值,你可以通过不再重复检索相同内容来减少数据和电池能耗。你还可以避免与其他客户端冲突,相反,在你需要调和差异时收到警告。
By embedding ETag
elements and Last-Modified
values in your DOM (or perhaps elsewhere for a native mobile app), you can then reduce the consumption of data and battery power by not retrieving the same thing over and over. You can also avoid colliding with other clients and, instead, be alerted when you need to reconcile differences.
通过这种方式,只需在你的前端进行一点调整和一些实体级别的编辑,后端就会提供你在为面向客户的客户端构建时可以兑现的时间敏感详情。
In this fashion, with just a little tweaking on your front end and some entity-level edits, the backend serves up time-sensitive details you can cash in on when building a customer-friendly client.