13. Conditional Requests (条件请求)
条件请求是一个HTTP请求,其中包含一个或多个请求头部字段,这些字段指示在将请求方法应用于目标资源之前要测试的前提条件。第13.2节定义了何时评估前提条件以及当存在多个前提条件时它们的优先顺序。
条件GET请求是HTTP缓存更新最有效的机制[CACHING]。条件也可以应用于状态改变方法,如PUT和DELETE,以防止"丢失更新"问题:一个客户端意外覆盖另一个并行工作的客户端的工作。
13.1. Preconditions (前提条件)
前提条件通常是针对目标资源作为整体的状态(其当前值集)或在先前获得的表示中观察到的状态(该集合中的一个值)来定义的。如果资源具有多个当前表示,每个表示都有自己的可观察状态,则前提条件将假设每个请求到所选表示的映射(第3.2节)在时间上是一致的。无论如何,如果映射不一致或服务器无法选择适当的表示,则当前提条件评估为false时不会造成任何损害。
下面定义的每个前提条件都包括将从目标资源的先前表示中获得的一组验证器与所选表示的验证器的当前状态(第8.8节)进行比较。因此,这些前提条件评估目标资源的状态自客户端已知的给定状态以来是否已更改。这种评估的效果取决于方法语义和条件的选择,如第13.2节中所定义。
由其他规范定义为扩展字段的其他前提条件可能会对所有接收者、目标资源的一般状态或一组资源施加条件。例如,如果接收者理解并实现该字段,则WebDAV中的"If"头部字段可以使请求依赖于多个资源的各个方面,例如锁([WEBDAV],第10.4节)。
只有当前提条件在未知时可以安全地忽略(如If-Modified-Since)、当可以假定给定用例的部署或当实现由目标资源的某些其他属性发出信号时,前提条件的可扩展性才是可能的。这鼓励关注共同商定的通用标准部署。
13.1.1. If-Match
"If-Match"头部字段使请求方法以接收方源服务器具有目标资源的至少一个当前表示(当字段值为"*"时)或具有目标资源的当前表示且其实体标签与字段值中提供的实体标签列表的成员匹配为条件。
源服务器在比较If-Match的实体标签时必须(MUST)使用强比较函数(第8.8.3.2节),因为客户端希望此前提条件在表示数据发生任何更改时阻止应用该方法。
If-Match = "*" / #entity-tag
示例:
If-Match: "xyzzy"
If-Match: "xyzzy", "r2d2xxxx", "c3piozzzz"
If-Match: *
If-Match最常与状态改变方法(例如,POST、PUT、DELETE)一起使用,以防止当多个用户代理可能并行作用于同一资源时发生意外覆盖(即,防止"丢失更新"问题)。通常,它可以与涉及选择或修改表示的任何方法一起使用,如果所选表示的当前实体标签不是If-Match字段值内的成员,则中止请求。
当源服务器接收到选择表示的请求并且该请求包括If-Match头部字段时,源服务器必须(MUST)在执行方法之前根据第13.2节评估If-Match条件。
评估接收到的If-Match头部字段:
-
如果字段值为"*",则如果源服务器具有目标资源的当前表示,则条件为真。
-
如果字段值是实体标签列表,则如果任何列出的标签与所选表示的实体标签匹配,则条件为真。
-
否则,条件为假。
评估If-Match条件的源服务器,如果条件评估为false,则不得(MUST NOT)执行请求的方法。相反,源服务器可以(MAY)通过以412(Precondition Failed)状态码响应来指示条件请求失败。
或者,如果请求是状态改变操作,则如果If-Match字段值中列出的一个或多个实体标签匹配所选表示的实体标签,源服务器可以(MAY)响应2xx(Successful)状态码(即,状态更改已经成功,因为资源的状态已经是期望的状态)。
如果客户端稍后将提供If-Match,则应该(SHOULD)在先前的请求中接收一个或多个实体标签,通常是通过从表示的ETag头部字段(第8.8.3节)获得。
13.1.2. If-None-Match
"If-None-Match"头部字段使请求方法以接收方源服务器不具有目标资源的当前表示,其实体标签与字段值中提供的任何实体标签匹配为条件。
接收包含If-None-Match头部字段的请求的接收方必须(MUST)使用弱比较函数来比较实体标签(第8.8.3.2节),因为弱实体标签可以在多次PUT操作之后的一致表示之间使用。
If-None-Match = "*" / #entity-tag
示例:
If-None-Match: "xyzzy"
If-None-Match: W/"xyzzy"
If-None-Match: "xyzzy", "r2d2xxxx", "c3piozzzz"
If-None-Match: W/"xyzzy", W/"r2d2xxxx", W/"c3piozzzz"
If-None-Match: *
If-None-Match主要用于两种情况:
-
与GET或HEAD一起使用以更新没有实体标签的缓存表示。如果源服务器接收到If-None-Match与GET或HEAD的请求,则应该(SHOULD)根据第13.2节评估它。
-
与其他方法一起使用以防止意外覆盖现有表示。如果请求会导致在没有前提条件的情况下覆盖表示,并且服务器具有该资源的当前表示,则客户端应该(SHOULD)发送If-None-Match字段值为"*"的请求。
当源服务器接收到选择表示的请求并且该请求包括If-None-Match头部字段时,源服务器必须(MUST)在执行方法之前根据第13.2节评估If-None-Match条件。
评估接收到的If-None-Match头部字段:
-
如果字段值为"*",则如果源服务器具有目标资源的当前表示,则条件为假。
-
如果字段值是实体标签列表,则如果列出的任何标签与所选表示的实体标签弱匹配(第8.8.3.2节),则条件为假。
-
否则,条件为真。
评估If-None-Match条件的源服务器:
-
如果请求方法是GET或HEAD,并且条件评估为false,则源服务器应该(SHOULD)生成304(Not Modified)响应。
-
如果请求方法不是GET或HEAD,并且条件评估为false,则源服务器必须(MUST)生成412(Precondition Failed)响应。
-
否则(即条件为真),源服务器应该(SHOULD)正常处理请求。
13.1.3. If-Modified-Since
"If-Modified-Since"头部字段使GET或HEAD请求方法以仅当目标资源自字段值中提供的日期和时间以来已被修改为条件。
If-Modified-Since = HTTP-date
示例:
If-Modified-Since: Sat, 29 Oct 1994 19:43:31 GMT
当接收方是源服务器并且If-Modified-Since是在GET或HEAD方法中接收的请求头部字段时,如果所选表示的Last-Modified日期早于或等于If-Modified-Since头部字段中的日期,则接收方应该(SHOULD)发送304(Not Modified)响应,除非根据第13.2节的评估会产生不同的响应。
接收方必须(MUST)忽略If-Modified-Since,如果请求包含If-None-Match头部字段;If-None-Match的条件在这种情况下被认为是对服务器意图的更准确替换,并且这两个条件彼此独立评估会阻碍其组合目的。
接收方应该(SHOULD)忽略If-Modified-Since头部字段,如果字段值中提供的时间戳是无效的或将来的。
13.1.4. If-Unmodified-Since
"If-Unmodified-Since"头部字段使请求方法以目标资源的选定表示自字段值中提供的日期和时间以来未被修改为条件。
If-Unmodified-Since = HTTP-date
示例:
If-Unmodified-Since: Sat, 29 Oct 1994 19:43:31 GMT
当接收方是源服务器并且If-Unmodified-Since是在除GET或HEAD之外的请求方法中接收的请求头部字段时,如果所选表示的Last-Modified日期早于或等于If-Unmodified-Since头部字段中的日期,则接收方应该(SHOULD)正常处理请求。否则,接收方应该(SHOULD)发送412(Precondition Failed)响应,除非根据第13.2节的评估会产生不同的响应。
接收方必须(MUST)忽略If-Unmodified-Since,如果请求包含If-Match头部字段;If-Match的条件在这种情况下被认为是对服务器意图的更准确替换,并且这两个条件彼此独立评估会阻碍其组合目的。
接收方应该(SHOULD)忽略If-Unmodified-Since头部字段,如果字段值中提供的时间戳是无效的或将来的。
13.1.5. If-Range
"If-Range"头部字段为Range请求(第14节)提供了一个特殊条件:如果提供的验证器与所选表示的当前验证器匹配,则以通常方式处理Range头部字段,返回一个或多个部分内容。如果验证器不匹配,则忽略Range头部字段,并返回完整的所选表示。
If-Range = entity-tag / HTTP-date
客户端不得(MUST NOT)在Range请求之外生成If-Range头部字段。
服务器必须(MUST)忽略在不包含Range头部字段的请求中接收的If-Range头部字段。
If-Range的意图是允许客户端在缓存的不完整表示已过时时有效地更新它。如果不存在If-Range头部字段,则不完整的缓存表示会强制客户端重新验证它(使用If-Match或If-Unmodified-Since条件请求),并仅在重新验证显示它仍然是当前的情况下请求缺失部分,或者替换整个缓存的表示。If-Range头部字段允许客户端通过在单个请求中请求当前部分或完整表示来避免这种额外的往返。
服务器使用强比较函数来评估If-Range条件(第8.8.3.2节),当字段值是实体标签时。当字段值是HTTP日期时,服务器将其与所选表示的Last-Modified字段值进行比较,如果时间戳严格相等则匹配。
If-Range头部字段应该(SHOULD)仅在If-Unmodified-Since与Last-Modified日期或If-Match与实体标签强匹配时才使用。
13.2. Evaluation of Preconditions (前提条件的评估)
13.2.1. When to Evaluate (何时评估)
除非其定义另有规定,否则服务器在执行方法之前必须(MUST)在接收选择表示的请求时评估接收的请求前提条件,因为它们的评估可能会影响方法的执行。
在执行方法之前评估前提条件的一个后果是,在方法已经开始执行之后收到的不同的前提条件将不会影响该执行或其响应语义。
这些前提条件可以在不同的选择步骤中进行评估。实际上,"选定的表示"的验证器通常在执行方法之前可用,因此前提条件可以在创建或修改表示之前进行评估。
13.2.2. Precedence of Preconditions (前提条件的优先级)
当多个条件请求头部字段存在于单个请求中时,应用这些前提条件的顺序变得重要。在实践中,作用于目标资源状态的字段通常一起部署,例如与If-Match一起使用If-Unmodified-Since,或与If-None-Match一起使用If-Modified-Since。但是,可能存在多个前提条件头部字段的不一致组合。
服务器必须(MUST)按照以下顺序评估接收的请求前提条件:
-
当接收方是源服务器并且If-Match存在时,如果评估为false,则评估If-Match前提条件并使用412(Precondition Failed)状态码响应。
-
当接收方是源服务器,If-Match不存在,并且If-Unmodified-Since存在时,如果评估为false,则评估If-Unmodified-Since前提条件并使用412(Precondition Failed)状态码响应。
-
当If-None-Match存在时,如果评估为false,则评估If-None-Match前提条件并使用304(Not Modified)状态码响应(对于GET/HEAD)或412(Precondition Failed)状态码响应(对于其他方法)。
-
当接收方是源服务器,方法是GET或HEAD,If-None-Match不存在,并且If-Modified-Since存在时,如果评估为false,则评估If-Modified-Since前提条件并使用304(Not Modified)状态码响应。
-
当方法是GET并且接收方具有Range头部字段的有效请求,并且If-Range存在时,如果验证器与所选表示的当前验证器匹配,则处理Range头部字段;否则,忽略Range头部字段并返回200(OK)响应和完整的所选表示。
-
否则,所有条件都已得到满足,因此正常执行请求的操作并生成适当的响应。
如果处理导致上述任何响应状态码,并且请求的源不是受信任的,则生成响应的服务器应该(SHOULD)在响应有效载荷中生成目标资源状态的表示。
对If-Match和If-Unmodified-Since头部字段的任何扩展需要等效地更新此前提条件评估算法以维持一致的行为。