4.6.3其他函数
parse_qs(qs[,keep_blank_values,strict_parsing])
这个函数的功能等同于标准库cgi parse_qs,但是是用C语言写的,运行更快。
转换一个query字符串(URL中附加的提交数据)作为字符串参数。数据以词典返回。词典的键名是query变量的变量名,值是对应变量的值的列表。
可选参数keep_blank_values是一个标志变量指定url编码中的空值是否作为空字符串处理。True指定空值转换为空白字符串。缺省值是False指定空值被忽略。
注意:strict_parsing参数尚未实现。
parse_qsl(qs[,keep_blank_values,strict_parsing])
这个函数功能等同于标准库cgi parse_qsl,但是是用C语言写的,速度更快。
转换一个query字符串,返回一个列表的数据,变量名和变量值对。
可选参数keep_blank_values和strict_parsing同上。
与上面函数的不同就是返回数据的格式。
redirect(req,location[,permanent=0,text=None])
这是一个可以方便的重定向浏览器到另外一个地址的函数。当permanent为True时,MOVED_PERMANENTLY状态被发送到客户端,或者是MOVED_TEMPORARILY。并发送一段简短的文本告知浏览器,文档已经被移走(当这个罕见的浏览器不支持重定向时);提示文本可以被text参数覆盖。
如果这个函数在响应头已经被发送之后调用,会触发IOError异常。
这个函数触发apache.SERVER_RETURN异常放弃之后的处理。如果不想这样,可以在redirect之外套一个try/except块来捕捉异常。
4.7Cookie-HTTP状态管理
Cookie模块提供了方便的(convenient)的方法来创建,分析,发送和接收HTTP Cookies,按照Netscape的定义。
注意:尽管RFC工作组描述了如何使用Cookie,但是实际上还是只有Netscape原始浏览器支持。而且,很多流行的浏览器还是兼容IETF标准的,而且即便声称RFC兼容的。因此,这个模块支持当前范例,而不是完全RFC兼容的。
在特殊情况下,Netscape与RFC的Cookie有很大的区别,比如路径和域名。Cookie模块忽略这种引入的属性,所以所以所有引入的cookie以Netscape风格的cookie告终,没有属性的定义。
参考:
客户端持久化状态-HTTP Cookies
http://wp.netscape.com/newref/std/cookie_spec.html
RFC 2109,"HTTP状态管理机制"
RFC 2964,"使用HTTP状态管理"
RFC 2965,"HTTP状态管理机制"
HTTP Cookies:标准,秘密与政见
http://arxiv.org/abs/cs.SE/0105018
4.7.1类定义
class Cookie(name,value[,attributes])
这个类用于构造一个单一的cookie名为name,值为value。在Netscape和RFC2109中定义的属性将作为关键字参数。
属性描述了cookie的属性,而他们的字符串将会成为cookie的一部分。Cookie类限定了属性名必须是有效值,如下是可用的属性名:name、 value、version、path、domain、secure、comment、expires、max_age 、commentURL、discard、port、__data__。
__data__属性是一个通用的词典可用于存储任意值,如果需要。在使用Cookie的子集时很重要。
expires属性。按照如下格式解释'Wdy, DD-Mon-YYYY HH:MM:S GMT' (按照每个Netscape cookie的定义),或者按照新纪元开始的秒数(自动转换为GMT时间字符串)。无效的expires值会抛出ValueError异常。
当转换到字符串时,一个Cookie将会被转换到Cookie或Set-Cookie头。
注意:不像Python标准库Cookie类,这个类指定单一cookie,等同于Python标准库中的Morsel。
parse(string)
这是一个类方法,用于从一个cookie字符串创建Cookie实例并传递到响应头的值。转换过程中,属性名将会转换为小写。
因为这是个类方法,所以必须通过类来调用。
这个方法返回Cookie实例的词典,不是单一的Cookie实例。
如下是获取单一Cookie实例的示例:
mycookies=Cookie.parse("spam=eggs; expires=Sat, "
14-Jun-2003 02:42:36 GMT")
spamcookie=mycookies["spam"]
注意:因为这个方法使用词典,所以不可能返回同名cookies。如果需要多个值在同一个cookie中,考虑使用MarshalCookie。
class SignedCookie(name,value,secret[,attributes])
这是Cookie的子类。这个类按照name和value自动创建一个经过HMAC (md5)签名过的Cookie,secret必须是非空字符串。
parse(string,secret)
这个方法同Cookie.parse(),但是cookie要被验证。如果签名的验证失败,则返回Cookie类。
注意:通常检查SignedCookie.parse()的返回类型。如果返回类型是Cookie(而不是SignedCookie),则签名验证失败:
#假设spam是一个签名过的cookie
if type(spam) is not Cookie.SignedCookie:
#签名验证失败的处理
class MarshalCookie(name,value,secret[,attributes])
这是SignedCookie的子类。允许value是任何的集结(marshallable)对象。Python核心(core)类型如字符串,整数,列表等,都是集结类型对象。完整的列表查看marshal模块文档。
转换过程中会检查签名,不正确的签名的cookie无法被集结(unmarshalled)。
4.7.2函数
add_cookie(req,cookie[,value,attributes])
向响应头方便的设置一个cookie。req是mod_python请求对象。如果cookie是一个Cookie类或其子类的实例。则cookie被设置,否则cookie参数必须是一个字符串,用于构造Cookie类的实例。cookie用作名字,value作为值,有效的Cookie属性作为关键字参数。
这个函数同时设置'Cache-Control: no-cache="set-cookie"'头来通知(inform)cookie值不需要缓存。
如下是使用这个函数的一种方法:
c=Cookie.Cookie('spam','eggs',expires=time.time()+300)
Cookie.add_cookie(req,c)
如下是另一个例子:
Cookie.add_cookie(req,'spam','eggs',expires=time.time()+"
300)
get_cookies(req[,Class,data])
从输入头获取(retrieving)cookie的方法。req是mod_python请求对象。Class是一个拥有parse()方法的用于转换成的cookie类,缺省是Cookie类。Data可以是任何关键字参数,将会被传递到parse()方法。对SignedCookie和MarshalCookie类等需要secret参数的parse()方法非常有用。
4.7.3例子
如下例子设置一个持续(expires)300秒的cookie:
from mod_python import Cookie,apache
import time
def handler(req):
cookie=Cookie.Cookie('eggs','spam')
cookie.expires=time.time()+300
Cookie.add_cookie(req,cookie)
req.write('此响应包含cookie!"n')
return apache.OK
如下例子检查输入的集结cookie并显示到客户端。如果输入没有cookie则设置一个cookie。这个例子使用'secret007'作为HMAC签名。
from mod_python import apache,Cookie
def handler(req):
cookies=Cookie.get_cookies(req,Cookie.MarshalCookie,"
secret='secret007')
if cookies.has_key('spam'):
spamcookie=cookies['spam']
req.write('Great, a spam cookie was found: %s"n'"
% str(spamcookie))
if type(spamcookie) is Cookie.MarshalCookie:
req.write('Here is what it looks like decoded'"
+':%s=%s"n'%(spamcookie.name,"
spamcookie.value))
else:
req.write('WARNING: The cookie found is not a "
MarshalCookie, it may have been tapered"
with!')
else:
#MarshalCookie允许值为任何集结对象
value={'egg_count':32, 'color':'white'}
Cookie.add_cookie(req,Cookie.MarshalCookie('spam',"
value,'secret007'))
req.write('Spam cookie not found, but we just"
set one!"n')
return apache.OK
4.8Session-对话管理
Session模块提供了跨越请求的持久化对象。
模块包含BaseSession类,但不可以直接使用。DbmSession类,使用dbm来存储对话。FileSession类,使用单独的文件存储对话。
BaseSession类也提供对话锁,支持进程和线程。锁定使用APR的global_mutexes函数。一定数量的锁在启动时预创建,互斥数用对话ID的hash()计算。因此就算两个对话具有不同的ID却有相同的哈希值,但仅有的联系(implication)是两个对话无法在同时被锁定。
4.8.1类
Session(req[,sid,secret,timeout,lock])
此函数拥有与BaseSession相同的参数。
这个函数返回一个缺省的对话实例。对话类可被用于PythonOption session value,这里的value可以是DbmSession、MemorySession或FileSession。自定义的对话类暂时还不被支持。
如果PythonOption session没有找到,则函数查询MPM和基于它的返回一个DbmSession或MemorySession实例。
MemorySession类将被用于MPM支持线程,但是不支持fork的情况,比如Windows,或者用于支持线程,fork,但是只允许一个进程的情况,比如MPM可以配置成这种。在其他所有的情况,使用DbmSession。
class BaseSession(req[,sid,secret,timeout,lock])
这个类意味着存储其他类并实现对话的存储机制。req是mod_python的请求对象的引用,是必须的。
BaseSession是dict类的子类。数据按照词典存取。
sid是一个可选的对话ID;如果提供则对话必须存在,忽略则会创建一个新的对话和新的sid。如果sid未提供,对象会在cookie中查找对话ID。如果找到了,但不是先前所指的,还会创建一个新的sid。判断是否是新创建的对话,调用is_new()方法。
Cookie有一个path属性用于比较DocumentRoot和目录在PythonHandler标志的作用中(病句,我自己也不懂)。比如,如果文档根目录是'/a/b/c',且PythonHandler指定为'/a/b/c/d/e',则路径会被设置为'/d/e'。也可以强制指定path使用ApplicationPath选项。即在服务器配置中的'PythonOption ApplicationPath /my/path选项。
当提供了secret参数时,BaseSession会使用SignedCookie来存储不可伪造(fake)的对话ID。缺省是简单的(plain)使用Cookie,假如没有被签名,则对话ID将会很难处理。
一个对话在超过timeout时间之后会失效而无法处理,缺省是30分钟。尝试装入期满(expired)的对话将会导致一个新的对话的产生。
lock参数缺省为1指定是否允许使用锁。当锁上时,只允许一个拥有相同对话ID的对话对象实例存在。
一个对话在刚创建时拥有"new"状态,而不通过cookie或sid参数来传递。
is_new()
如果返回1,则对话是刚刚创建的。在发生超时或不存在的sid时,也会新创建一个对话。使用这个方法来测试对话是否为新是很有用的:
sess=Session(req)
if sess.is_new():
#重定向到登录
util.redirect(req,'http://www.mysite.com/login')
id()
返回对话ID
created()
返回对话的创建时间,按照从新纪元(epoch)开始的秒数。
last_accessed()
返回上次存取时间,按照从新纪元开始的秒数。
timeout()
返回以秒为单位的超时间隔(timeout interval)。
set_timeout(secs)
设置超时时间为secs秒。
invalidate()
删除对话,并发送响应头要求删除客户端的对话相关cookie。
load()
从存储装入对话的值。
save()
将对话值写入存储。
delete()
从存储中删除对话。
init_lock()
初始化对话锁。无需每次都调用这个方法,仅在子类打算(intend)使用互斥锁(alternative lock)时使用。
lock()
锁住对话。如果对话已经被其他线程/进程锁住,则等待到锁被释放(release)时。锁是被自动控制的,而无需调用这个方法,缺省。这个方法也注册一个清理行为,在请求处理结束时解锁一个对话。
unlock()
解锁对话。同lock()当自动处理时(缺省),无需手工调用。
cleanup()
这个方法供子类实现对话的存储清理机制(比如删除超时对话等)。它将会被随机调用,调用的机会由CLEANUP_CHANGE变量,一个Session模块的成员来控制,缺省值为1000。这意味着被安排好(be ordered)的随机清理行为发生概率在1/1000的机会(chance)。子类实现这个方法之后,将不会按照时间强制的清理,但将会需要用req.register_cleanup注册清理行为来代替,执行请求之后的处理。
class DbmSession(req[,dbm,sid,secret,dbmtype,timeout,lock])
这个类提供了使用dbm文件来存储对话的方法,dbm存储速度很快,且大多数dbm实现使用内存映射文件来提供更高的存取速度,其响应性能接近使用共享内存。
dbm是dbm文件名,且这个文件必须可被httpd进程写入。这个文件在服务器进程停止之后不会被删除,一个好处是服务器重启后对话仍然存在(survive)。缺省时对话信息保存在'mp_sess.dbm'文件中。也可在配置标志中指定路径PythonOption session_directory标志。路径和文件名可以在如下设置中重载PythonOption session_dbm filename。
当使用Python的anydbm模块实现时,在多数系统中缺省使用dbhash模块。如果需要特并的指定dbm的实现,如gdbm等,可以传递dbmtype参数。
注意这个类的使用不是跨平台的。具有最好的跨平台兼容性(compatibility)的,还是使用Session()来创建对话。
class FileSession(req[,sid,secret,timeout,lock,
fast_cleanup,verify_cleanup])
这是在版本3.2.0开始实现的。
此类在文件中存储对话数据,是BaseSession的子类。
对话数据存储在特定文件中。这些文件在服务器停止时不会被删除,所以对话可以在服务器重启之间继续有效。对话文件保存在由tempfile.gettempdir()标准库函数返回的临时目录下的mp_sess目录下。另一个可选路径是使用PythonOption session_directory /path/to/dir 来设置,并且这个目录必须存在且可用apache进程读写。
过期的对话文件将会被清理机制按周期(periodically)删除。清除行为受到fast_cleanup和verify_cleanup参数控制,也可以用PythonOption session_grace_period和PythonOption session_cleanup_time_limit来控制。
fast_cleanup:布尔值,用于开启FileSession清理优化。缺省是True并将在清除大量的对话文件时减少清理时间。当fast_cleanup为True时,文件的修改时间用于决定(determine)被删除的候选名单(candidate)。如果(current_time - file_modification_time) > (timeout + grace_period),则文件将会加入删除候选名单。如果verify_cleanup为False,不需要深入的检查而直接删除。
如果fast_cleanup为False,对话文件将会被非酸洗(unpickled?)并且超时(timeout)被用于决定对话是否要加入删除候选名单。fast_cleanup=Fasle意味着(imply)verify_cleanup=True。
超时(timeout)被用于计算请求的对话中哪些到达了超时,以用于调用filesession_cleanup。如果对话对象没有相同的超时,或者手动设定了特定(particular)对话的超时,需要设置verify_cleanup=True。
fast_cleanup的值也可以用PythonOption session_fast_cleanup来设置。
verify_cleanup:布尔值,用于优化FileSession的清理行为。缺省为True。如果为True,则正在被考虑的对话文件将会参考超时值决定是否要删除。当为False时当前对话的超时值将被用于决定对话是否超期(expire)。在这种情况下,对话数据被读取,这将会在含有大量对话文件时提高性能,或者在每个对话存取大量数据时。这将会导致当前对话拥有一个独一无二的超时值时被删除(什么鬼话?)。这个值也可用PythonOption session_verify_cleanup来设置。
PythonOption session_cleanup_time_limit[value]:整数值秒数。缺省为2秒。对话的清理潜在的(potentially)耗费大量耗费CPU和硬盘读取时间,与对话文件数量和数据量有关。为了避免(avoid)服务器超负荷(overload),每次filesession_cleanup运行时都在达到session_cleanup_time_limit时进行。每次清理将会从上次清理的最后开始。设置这个值为0将会关闭这个功能,在每次调用filesession_cleanup时会完成这个功能。
PythonOption session_grace_period[value]:整数值秒数。缺省为240秒。用于在测试对话文件时增加超时时间。一个对话正在被其他请求读取时正巧发生清理行为的可能性很小。有可能在一个请求要存入对话文件时而另一个请求要立即删除。为了避免这种竞争状态,对话允许在确定清理之前保留一个grace_period周期。这个周期的时间比任何请求完成的时间都要长,但一般还是短于1秒,以确保对话在被删除之前不会出错。缺省值适合(sufficient)大多数应用。
class MemorySession(req[,sid,secret,timeout,lock])
这个类提供在全局词典中保存对话。提供更好的性能,但是需要多进程配置的支持,也会为每个对话耗费(consume)一定的内存。
注意使用这个类并不是跨平台的。为了跨平台,最好还是使用Session()函数来创建对话。
4.8.2例子
如下是记录点击次数的简单例子(demonstrate)。
from mod_python import Session
def handler(req):
session=Session.Session(req)
try:
session['hits']+=1
except:
session['hits']=1
session.save()
req.content_type='text/plain'
req.write('Hits: %d"n' % session['hits'])
return apache.OK
4.9psp-Python服务器端页面
psp模块提供一种从包含Python代码的文本(包含,但是不限于HTML文档),转换到适用于mod_python处理器的Python代码。提供一种类似于ASP,JSP等的动态内容嵌入技术。
psp的转换器使用C语言书写(使用flex),所以运行速度很快。
查看6.2节"PSP处理器"了解更多PSP的信息。
在文档内,Python代码需要被'<%'和'%>'括起来。Python表达式需要附上(enclosed)'<%='和'%>'。标志需要被'<%@'和'%>'括起来。作为代码一部分的注释需用'<%--'和'--%>'括起来。
如下是原始的PSP页面用于示例(demonstrate)同时包含代码和表达式的嵌入HTML文档的例子:
<html>
<%
import time
%>
Hello world, the time is: <%=time.strftime("%Y-%m-%d,"
%H:%M:%S")%>
</html>
在内部,PSP转换器翻译如上页面到如下的Python代码:
req.write("""<html>
""")
import time
req.write("""
Hello world, the time is: """)
req.write(str(time.strftime("%Y-%m-%d, %H:%M:%S")))
req.write("""</html>
""")
这些代码将会在处理器的返回中显示:'Hello world, the time is: '加上当前时间。
Python代码可以被用于输出部分页面条件(conditionally)循环。代码将被显示成锯齿状。在下一段以锯齿括起的Python代码开始时,循环将被结束。哪怕这段代码只是一段注释。
如下是例子:
<html>
<%
for n in range(3):
# this indent will persist
%>
<p>This paragraph will be
repeated 3 times.</p>
<%
# this line will cause the block to end
%>
This line will only be shown once.<br>
</html>
如下是内部自动生成的Python代码:
req.write("""<html>
""")
for n in range(3):
# this indent will persist
req.write("""
<p>This paragraph will be
repeated 3 times.</p>
""")
# this line will cause the block to end
req.write("""
This line will only be shown onece.<br>
</html>
""")
转换器也会聪明的在以冒号结尾的Python代码后,识别新的'<%%>'对来结束重置代码快,跳出循环。如下页面与如上的最终效果相同:
<html>
<%
for n in range(3):
%>
<p>This paragraph will be
repeated 3 times.</p>
<%
%>
This line will only be shown once.<br>
</html>
无论如上代码如何混淆(confuse),这样用注释来结束代码块还是相当推荐的好习惯。
现在支持的唯一一个标志是include,如下用法:
<%@ include file="/file/to/include"%>
函数parse()会被按照dir参数调用,并把文件转换到绝对路径。
class PSP(req[,filename,string,vars])
这个类声明(represent)了一个PSP对象。
req是请求对象,filename和string是可选参数指定PSP源代码。但是只有一个会生效,如果没有指定则用req.filename来指定filename。
vars是一个全局变量词典。传递run()方法覆盖这里的vars参数。
这个类被PSP处理器在内部调用,但也可以被模板工具使用。
当使用文件作为源时,代码对象将会返回缓存中的文件对象和文件修改时间。缓存是作用于全局的Python解释器。所以除非文件修改时间改变了,文件无需重新编译和解释。
缓存限制到512个页面,依赖于可用内存总量。如果与内存有关,可以选择使用dbm文件缓存。作者简单的测试表明,使用bsd db之后大约性能慢了20%。也需要测试anydbm在你的平台上的缺省实现对大小的限制是否合适。dbm缓存允许通过PSPDbmCache这个PythonOption,例如:
PythonOption PSPDbmCache ''/tmp/pspcache.dbm''
注意,dbm缓存在服务器重启时是不会被删除的。
不像使用文件,从字符串来的代码对象只会缓存在内存中。无需选项来缓存dbm文件。
run([vars])
这个方法将会执行代码(由对象初始化时转换和编译过的PSP代码)。可选参数vars是一个用于传送全局变量的词典。另外,PSP代码可以使用全局变量如req,psp,session和form。一个对话将被创建,并可以在PSP代码中使用session变量(PSP代码检查co_names代码对象来做决定)。记住在处理session和打开对话锁时会有一些阻塞。类似的,mod_python的FieldStorage对象将会实例化为form对象。PSPInstance类会传递psp对象作为实例。