跳转到主要内容

此软件包与zope publisher集成,根据`If-None-Match`、`If-Match`、`If-Modified-Since`和`If-UnModifiedSince`协议验证条件请求。

项目描述

z3c.condtionalviews

z3c.conditionalviews是一个机制,用于根据一些条件协议(如实体标签或最后修改日期)验证HTTP请求。它也是可扩展的,因此像WebDAV这样的协议可以定义自己的条件协议,如IF头。

它通过将每个条件协议实现为一个IHTTPValidator实用工具来工作,请参阅etag和lastmodification模块以了解最常见的情况。然后当发布者调用某些视图时,我们查找这些实用工具,并要求它们根据实用工具实现的协议验证请求对象。

在调用视图时,以及在验证请求时,我们通常可以访问上下文、请求和视图本身。因此,IHTTPValidator实用工具通常将这些3个对象适配到实现特定于所讨论协议的接口的对象。例如,实体标签验证器查找实现IEtag的适配器。

Zope集成

>>> import zope.component
>>> import zope.interface
>>> import z3c.conditionalviews.interfaces
>>> import z3c.conditionalviews.tests

装饰器

为了集成可缓存的通用浏览器视图,我们可以使用 z3c.conditionalviews.ConditionalView 对象装饰视图的调用方法。请注意,本测试中使用的所有视图都定义在 ftesting.zcml 文件中。

>>> response = http(r"""
... GET /@@simpleview.html HTTP/1.1
... Host: localhost
... """, handle_errors = False)
>>> response.getStatus()
200
>>> response.getHeader('content-length')
'82'
>>> response.getHeader('content-type')
'text/plain'
>>> print response.getBody()
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

因为我们还没有定义实现 IETag 的适配器,所以响应中不包含 ETag 标头。

>>> response.getHeader('ETag') is None
True

定义我们的 IETag 实现。

>>> class SimpleEtag(object):
...    zope.interface.implements(z3c.conditionalviews.interfaces.IETag)
...    def __init__(self, context, request, view):
...        pass
...    weak = False
...    etag = "3d32b-211-bab57a40"
>>> zope.component.getGlobalSiteManager().registerAdapter(
...    SimpleEtag,
...    (zope.interface.Interface,
...     zope.publisher.interfaces.browser.IBrowserRequest,
...     zope.interface.Interface))
>>> response = http(r"""
... GET /@@simpleview.html HTTP/1.1
... Host: localhost
... """, handle_errors = False)
>>> response.getStatus()
200
>>> response.getHeader('content-length')
'82'
>>> response.getHeader('content-type')
'text/plain'
>>> response.getHeader('ETag')
'"3d32b-211-bab57a40"'
>>> print response.getBody()
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

现在通过设置请求头 If-None-Match: “3d32b-211-bab57a40”,我们的视图验证失败,并返回 304 响应。

>>> response = http(r"""
... GET /@@simpleview.html HTTP/1.1
... Host: localhost
... If-None-Match: "3d32b-211-bab57a40"
... """, handle_errors = False)
>>> response.getStatus()
304
>>> response.getHeader('ETag')
'"3d32b-211-bab57a40"'
>>> response.getBody()
''

XXX - 这看起来不正确,响应的内容长度和内容类型不应该为此响应设置。

>>> response.getHeader('content-length')
'0'
>>> response.getHeader('content-type')
'text/plain'

现在确保我们没有破坏发布者,确保我们仍然可以向不同的视图传递参数。

>>> response = http(r"""
... GET /@@simpleview.html?letter=y HTTP/1.1
... Host: localhost
... """, handle_errors = False)
>>> response.getStatus()
200
>>> response.getHeader('content-length')
'82'
>>> print response.getBody()
yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy

我们现在为这个请求得到一个 charset 值,因为 SimpleView 的默认值不是一个 unicode 字符串,而请求接收到的数据默认情况下会自动转换为 unicode。

>>> response.getHeader('content-type')
'text/plain;charset=utf-8'

由于请求中存在查询字符串,我们没有设置 ETag 标头。

>>> response.getHeader('ETag') is None
True

以下请求中的查询字符串使请求有效,否则它将无效。

>>> response = http(r"""
... GET /@@simpleview.html?letter=y HTTP/1.1
... If-None-Match: "3d32b-211-bab57a40"
... Host: localhost
... """, handle_errors = False)
>>> response.getStatus()
200

通用 HTTP 条件发布

我们可以将验证方法与发布调用方法集成。这将对通过发布 callObject 方法传递的每个请求进行验证。这对于验证修改对象的请求很有用,以便客户端可以表示如果资源自上次下载以来没有改变,则可以修改此资源,或者如果请求 URI 指定的位置不存在现有资源。

这还有一个额外的好处,我们不必指定如何实现 PUT 方法。

>>> resp = http(r"""
... PUT /testfile HTTP/1.1
... Authorization: Basic mgr:mgrpw
... Content-type: text/plain
... Content-length: 55
... aaaaaaaaaa
... aaaaaaaaaa
... aaaaaaaaaa
... aaaaaaaaaa
... aaaaaaaaaa""", handle_errors = False)
>>> resp.getStatus()
201
>>> resp.getHeader('Content-length')
'0'
>>> resp.getHeader('Location')
'http://localhost/testfile'
>>> resp.getHeader('ETag', None) is None
True

我们现在可以获取资源和实体标签。

>>> resp = http(r"""
... GET /testfile HTTP/1.1
... Authorization: Basic mgr:mgrpw
... """)
>>> resp.getStatus()
200
>>> resp.getHeader('ETag')
'"testfile:1"'
>>> print resp.getBody()
aaaaaaaaaa
aaaaaaaaaa
aaaaaaaaaa
aaaaaaaaaa
aaaaaaaaaa

我们本来可以使用 HEAD 方法来获取实体标签。

>>> resp = http(r"""
... HEAD /testfile HTTP/1.1
... Authorization: Basic mgr:mgrpw
... """)
>>> resp.getStatus()
200
>>> resp.getHeader('ETag')
'"testfile:1"'

没有 ‘If-None-Match’ 标头,我们将覆盖数据。

>>> resp = http(r"""
... PUT /testfile HTTP/1.1
... Authorization: Basic mgr:mgrpw
... Content-type: text/plain
... Content-length: 55
... bbbbbbbbbb
... bbbbbbbbbb
... bbbbbbbbbb
... bbbbbbbbbb
... bbbbbbbbbb""", handle_errors = False)
>>> resp.getStatus()
200
>>> resp.getHeader('Content-length')
'0'
>>> resp.getHeader('Location', None) is None
True
>>> resp.getHeader('ETag')
'"testfile:2"'
>>> resp = http(r"""
... GET /testfile HTTP/1.1
... Authorization: Basic mgr:mgrpw
... """)
>>> resp.getStatus()
200
>>> print resp.getBody()
bbbbbbbbbb
bbbbbbbbbb
bbbbbbbbbb
bbbbbbbbbb
bbbbbbbbbb

指定 If-None-Match: “*” 标头,表示只有在请求 URI 指定的位置不存在资源时才上传数据。如果该位置存在资源,则返回 412 Precondition Failed 响应,并且资源不会被修改。

>>> resp = http(r"""
... PUT /testfile HTTP/1.1
... Authorization: Basic mgr:mgrpw
... If-None-Match: "*"
... Content-type: text/plain
... Content-length: 55
... cccccccccc
... cccccccccc
... cccccccccc
... cccccccccc
... cccccccccc""")
>>> resp.getStatus()
412
>>> resp.getHeader('Content-length')
'0'
>>> resp.getHeader('Location', None) is None
True
>>> resp.getHeader('ETag')
'"testfile:2"'

文件没有更改。

>>> resp = http(r"""
... GET /testfile HTTP/1.1
... Authorization: Basic mgr:mgrpw
... """)
>>> resp.getStatus()
200
>>> print resp.getBody()
bbbbbbbbbb
bbbbbbbbbb
bbbbbbbbbb
bbbbbbbbbb
bbbbbbbbbb

现在由于 testfile2 还不存在,我们包含内容。

>>> resp = http(r"""
... PUT /testfile2 HTTP/1.1
... Authorization: Basic mgr:mgrpw
... If-None-Match: "*"
... Content-type: text/plain
... Content-length: 55
... yyyyyyyyyy
... yyyyyyyyyy
... yyyyyyyyyy
... yyyyyyyyyy
... yyyyyyyyyy""")
>>> resp.getStatus()
201
>>> resp.getHeader('Content-length')
'0'
>>> resp.getHeader('Location')
'http://localhost/testfile2'
>>> resp.getHeader('ETag', None) is None # No etag adapter is configured
True
>>> resp = http(r"""
... GET /testfile2 HTTP/1.1
... Authorization: Basic mgr:mgrpw
... """)
>>> resp.getStatus()
200
>>> print resp.getBody()
yyyyyyyyyy
yyyyyyyyyy
yyyyyyyyyy
yyyyyyyyyy
yyyyyyyyyy

我们现在可以删除资源,前提是它没有更改。所以对于 ‘/testfile’ 资源,我们可以使用其第一个实体标签来确认这一点。

>>> resp = http(r"""
... DELETE /testfile HTTP/1.1
... Authorization: Basic mgr:mgrpw
... If-Match: "testfile:1"
... """)
>>> resp.getStatus()
412

并且文件仍然存在。

>>> resp = http(r"""
... GET /testfile HTTP/1.1
... Authorization: Basic mgr:mgrpw
... """)
>>> resp.getStatus()
200

但是使用有效的实体标签我们可以删除资源。

>>> resp = http(r"""
... DELETE /testfile HTTP/1.1
... Authorization: Basic mgr:mgrpw
... If-Match: "testfile:2"
... """)
>>> resp.getStatus()
200
>>> resp.getBody()
''
>>> resp = http(r"""
... GET /testfile HTTP/1.1
... Authorization: Basic mgr:mgrpw
... """)
>>> resp.getStatus()
404

方法不被允许

对于尚未注册的方法,我们仍然应该得到 405 Method Not Allowed 状态。

我们需要登录才能遍历到文件。

>>> resp = http(r"""
... FROG /testfile2 HTTP/1.1
... Authorization: Basic mgr:mgrpw
... """)
>>> resp.getStatus()
405
>>> resp.getHeader('ETag', None) is None
True

清理

>>> zope.component.getGlobalSiteManager().unregisterAdapter(
...    SimpleEtag,
...    (zope.interface.Interface,
...     zope.publisher.interfaces.browser.IBrowserRequest,
...     zope.interface.Interface))
True

z3c.conditionalviews 的更改

1.0 (2008-09-27)

  • 使用 IDCTimes 而不是 IZopeDublinCore,因为 IDCTimes 是实际所需的接口。

1.0b

  • 修复了处理 If-Modified 协议时的时区问题。

0.9

  • 初始发布。

项目详情


下载文件

为您的平台下载文件。如果您不确定选择哪个,请了解更多关于 安装包 的信息。

源分布

z3c.conditionalviews-1.0.tar.gz (16.3 kB 查看哈希)

上传

支持者

AWSAWS云计算和安全赞助商DatadogDatadog监控FastlyFastlyCDNGoogleGoogle下载分析MicrosoftMicrosoftPSF赞助商PingdomPingdom监控SentrySentry错误记录StatusPageStatusPage状态页面