此包与zope发布服务器集成,根据“if none match”、“if match”和“if modified since”、“if unmodified since”协议验证条件请求。
z3c.conditionalviews的Python项目详细描述
z3c.条件视图
z3c.conditionalviews是一种基于 条件协议,如实体标记或上次修改日期。它也是 可扩展,以便像webdav这样的协议可以定义自己的条件 像if头这样的协议。
它通过将每个条件协议实现为ihttpvalidator 实用程序,请参阅etag和lastmodify模块以了解最常见的用例。 然后当发布者调用某些视图时,我们查找这些实用程序 让他们根据任何协议验证请求对象 实用工具实现。
在调用视图时,当我们验证请求时,我们 通常可以访问上下文、请求和查看自身。所以 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
现在通过设置请求头如果没有匹配:“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
我们现在正在为此请求获取一个字符集值,因为 当接收到数据时,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方法。这对于验证 修改对象,以便客户端可以说如果没有修改此资源 上次下载资源后发生更改,或者如果不存在 某个位置的资源。
这有额外的好处,我们不需要指定 实现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中指定的位置没有资源。如果有 资源,然后a412 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
现在既然TestFiel2确实存在,我们就满足了内容。
>>> 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.conditionalView中的更改
1.0(2008-09-27)
- 使用idctimes而不是izopedublincore,因为idctimes是实际的 必需的接口。
1.0b
- 修正了处理协议中的时区问题。
0.9
- 初次发布。