二、高级应用

2.1 web.ctx

获取客户端信息,比如:来源页面、客户端浏览器类型等。

web.ctx基于 threadeddict类,又被叫做 ThreadDict。这个类创建了一个类似字典(dictionary-like)的对象,对象中的值都是与线程 id 相对应的。这样做很妙,因为很多用户同时访问系统时,这个字典对象能做到仅为某一特定的 HTTP 请求提供数据(因为没有数据共享,所以对象是线程安全的)。

web.ctx保存每个HTTP请求的特定信息,比如客户端环境变量。假设,我们想知道正在访问某页面的用户从哪个网页跳转而来的:

class example:
def GET(self):
referer = web.ctx.env.get("HTTP_REFERER", "http://gogle.com")
raise web.seeother(referer)

上述代码用 web.ctx.env 获取 HTTP_REFERER 的值。如果 HTTP_REFERER 不存在,就会将 google.com 做为默认值。接下来,用户就会被重定向回到之前的来源页面。

web.ctx 另一个特性,是它可以被 loadhook 赋值。例如:当一个请求被处理时,会话(Session)就会被设置并保存在 web.ctx 中。由于 web.ctx是线程安全的,所以我们可以象使用普通的 python 对象一样,来操作会话(Session)。

ctx中的数据成员
request
environ 又被写作.evn -- 包含标准WSGI环境变量的字典
home -- 应用的http根路径(注释:可以理解为应用的起始网址,协议+站点域名+应用所在路径)例如:http://example.org/admin
homedomain -- 应用所在站点(可以理解为协议+域名)http://example.org
host -- 主机名(域名)+用户请求的端口(如果没有的话,就是默认的 80 端口),例如: example.org, example.org:8080
ip –- 用户的 IP 地址,例如: xxx.xxx.xxx.xxx
method – 所用的 HTTP 方法,例如: GET
path –- 用户请求路径,它是基于当前应用的相对路径。在子应用中,匹配外部应用的那部分网址将被去掉。例如:主应用在 code.py中,而子应用在 admin.py 中。在 code.py中, 我们将/admin 关联到admin.app。 在 admin.py 中, 将/stories 关联到 stories类。在stories 中, web.ctx.path 就是/stories, 而非/admin/stories。形如: /articles/845
protocol –- 所用协议,例如: https
query –- 跟在'?'字符后面的查询字符串。如果不存在查询参数,它就是一个空字符串。例如: ?fourlegs=good&twolegs=bad
fullpath 可以视为 path + query – 包查询参数的请求路径,但不包括'homepath'。例如:/articles/845?fourlegs=good&twolegs=bad response
status –- HTTP 状态码(默认是'200 OK') 401 Unauthorized 经授权
headers –- 包 HTTP 头信息(headers)的二元组列表。
output –- 包响应实体的字符串。

2.2 应用处理器(Application processors)

使用应用处理器加载钩子(loadhook)和卸载钩子(unloadhook)。

web.py可以在处理请求之前或之后,通过添加处理器(processor)来完成某些操作:

def my_processor(handler):
print "before handling"
result = handler()
print "after handling"
return result app.add_processor(my_processor)

web.py也可以通过加载钩子(loadhook)和卸载钩子(unloadhook)的方式来完成同样的操作,他们分别在请求开始和请求结束工作:

def my_loadhook():
print "my load hook" def my_unloadhook():
print "my unload hook" app.add_processor(web.loadhook(my_loadhook))
app.add_processor(web.unloadhook(my_unloadhook))

上边两个例子,如果加在主应用里面,则主应用和子应用的接口都会加上这个处理器;如果只在某个子应用里面加这个处理器,则只有这个子应用的接口被加了这个处理器。如果一个加在主应用,另一个加载子应用,则主应用的属于全局,不论访问主应用合适访问子应用都要走;子应用的不仅要走主应用的处理器,还要走自己的处理器。顺序如下:主befor 》子befor 》子after 》主after

2.3 web.background

web.background 和 web.backgrounder 都是 python 装饰器,它可以让某个函式在一个单独的 background 线程中运行,而主线程继续处理当前的 HTTP 请求,并在稍后报告 background 线程的状态(事实上,后台函式的标准输出(stdout)被返回给启动该线程的"backrounder")。 译注:我本来想将 background thread 翻译为后台线程,后来认为作者本意是想表达“被 background 修饰的函式所在的线程”,最后翻译采用“background 线程”。 这样,服务器就可以在处理其他 http 请求的同时,快速及时地响应当前客户端请求。同时,background 线程继续执行需要长时间运行的函式。

#!/usr/bin/env python
# -*- coding: utf-8 -*- from web import run, background, backgrounder
from datetime import datetime; now = datetime.now
from time import sleep urls = (
'/', 'index',
) class index:
@backgrounder
def GET(self):
print "Started at %s" % now()
print "hit f5 to refresh!"
longrunning() @background
def longrunning():
for i in range(10):
sleep(1)
print "%s: %s" % (i, now()) if __name__ == '__main__':
run(urls, globals())

在请求 http://localhost:8080/时,将自动重定向到类似http://localhost:8080/?_t=3080772748 的网址(t 后面的数字就是background 线程 id),接下来(在点击几次刷新之后)就会看到如下信息:

Started at 2008-06-14 15:50:26.764474
hit f5 to refresh!
0: 2008-06-14 15:50:27.763813
1: 2008-06-14 15:50:28.763861
2: 2008-06-14 15:50:29.763844
3: 2008-06-14 15:50:30.763853
4: 2008-06-14 15:50:31.764778
5: 2008-06-14 15:50:32.763852
6: 2008-06-14 15:50:33.764338
7: 2008-06-14 15:50:34.763925
8: 2008-06-14 15:50:35.763854
9: 2008-06-14 15:50:36.763789

web.py 在 background.threaddb 字典中保存线程信息。这就很容易检查线程的状态:

class threaddbviewer:
def GET(self):
for k, v in background.threaddb.items():
print "%s - %s" % ( k, v )

web.py 并不会主动去清空 threaddb 词典,这使得输出(如http://localhost:8080/?_t=3080772748)会一直执行,直到内存被用满。
通常是在 backgrounder 函式中做线程清理工作,是因为 backgrounder可以获得线程 id(通过 web.input()得到"_t"的值,就是线程 id),从而根据线程 id 来回收资源。这是因为虽然 background 能知道自己何时结束,但它无法获得自己的线程 id,所以 background 无法自己完成线程清理。
还要注意 How not to do thread local storage with Python 在 python 中如何避免多线程本地存储 - 线程 ID 有时会被重用(可能会引发错误) 。

在使用 web.background 时,还是那句话--“小心为上”!

2.4 自定义NotFound消息

import web

urls = (
...
) app = web.application(urls, globals()) def notfound():
return web.notfound("Sorry, the page you were looking for was not found!"
# return web.notfound(render.notfound())
# return web.notfound(str(render.notfound())) app.notfound = notfound

要返回自定义的NotFound消息,这么做即可:

class example:
def GET(self):
raise web.notfound()

这个返回的是 404 Not Found
也可以用同样的方法自定义500错误消息:

def internalerror():
return web.internalerror("Bad, bad server. No donut for you." app.internalerror = internalerror

2.5 使用流来传输大文件

要用流的方式传输大文件,需要添加传输译码(Transfer-Eencoding)区块头,这样才能一边下载一边显示。否则,浏览器将缓存所有数据直到下载完毕才显示。

如果这样写:直接修改基础字符串(例中就是j),然后用yield返回——是没有效果的。如果要用yield,就要向所有内容使用yield。因为这个函式此时是一个产生器。

# coding: utf-8
# 简单流式服务器演示
# 使用time.sleep模拟大文件读取 import web
import time

urls = (    
"/", "count_holder",    
"/(.*)", "count_down",    
)
app = web.application(urls, globals()) class count_down:
def GET(self, count):
# 这些头使它在浏览器中工作
web.header("Content-Type", "text/html")
web.header("Transfer-Encoding", "chunked")
yield "<h2>Prepare for Launch!</h2>"
j = "<li>Liftoff in %s...</li>"
yield "<ul>"
count = int(count)
for i in range(count, 0, -1):
out = j % i
time.sleep(1)
yield out
yield "</ul>"
time.sleep(1)
yield "<h1>Life off</h1>" class count_holder:
def GET(self):
web.header("Content-Type", "text/html")
web.header("Transfer-Encoding", "chunked")
boxes = 4
delay = 3
countdown = 10
for i in range(boxes):
output = "<iframe src='/%d' width='200' height='500'></iframe>"%(countdown - i)
yield output
time.sleep(delay) if __name__ == "__main__":
app.run()

2.6 管理自带的webserver日志

我们可以用wsgilog来操作内置的webserver的日志,并将其作为中间件加到应用中。

写一个Log类继承wsgilog.WsgiLog,在__init__中把参数传给基类。

如下:

import sys, logging
from wsgilog from WsgiLog, LogI0
import config class Log(WsgiLog):
def __init__(self, application):
WsgiLog.__init__(
self,
application,
logformat = "%(message)s",
tofile = True,
file = config.log_file,
interval = config.log_interval,
backups = config.log_backups
)
sys.stdout = LogIO(self.logger, logging.INFO)
sys.stderr = LogIO(self.logger, logging.ERROR)

接下来,当应用运行时,传递一个引用给上例中的 Log 类即可(假设上面代码是'mylog'模块的一部分,代码如下):

from mylog import Log
application = web.application(urls, globals())
application.run(Log)

2.7 用cheerypy提供SSL支持

import web
from web.wsgiserver import CherryPyWSGIServer CherryPyWSGIServer.ssl_certificate = "path/to/ssl_certificate"
CherryPyWSGIServer.ssl_private_key = "path/to/ssl_private_key" urls = ("/.*", "hello")
app = web.application(urls, globals()) class hello:
def GET(self):
return 'Hello, world!' if __name__ == "__main__":
app.run()

2.8 实时语言切换

首先你必须阅读模板语言中的i18n支持, 然后尝试下面的代码。

文件: code.py

import os
import sys
import gettext
import web # File location directory.
rootdir = os.path.abspath(os.path.dirname(__file__)) # i18n directory.
localedir = rootdir + '/i18n' # Object used to store all translations.
allTranslations = web.storage() def get_translations(lang='en_US'):
# Init translation.
if allTranslations.has_key(lang):
translation = allTranslations[lang]
elif lang is None:
translation = gettext.NullTranslations()
else:
try:
translation = gettext.translation(
'messages',
localedir,
languages=[lang],
)
except IOError:
translation = gettext.NullTranslations()
return translation def load_translations(lang):
"""Return the translations for the locale."""
lang = str(lang)
translation = allTranslations.get(lang)
if translation is None:
translation = get_translations(lang)
allTranslations[lang] = translation # Delete unused translations.
for lk in allTranslations.keys():
if lk != lang:
del allTranslations[lk]
return translation def custom_gettext(string):
"""Translate a given string to the language of the application."""
translation = load_translations(session.get('lang'))
if translation is None:
return unicode(string)
return translation.ugettext(string) urls = (
'/', 'index'
) render = web.template.render('templates/',
globals={
'_': custom_gettext,
}
) app = web.application(urls, globals()) # Init session.
session = web.session.Session(app,
web.session.DiskStore('sessions'),
initializer={
'lang': 'en_US',
}
) class index:
def GET(self):
i = web.input()
lang = i.get('lang', 'en_US') # Debug.
print >> sys.stderr, 'Language:', lang session['lang'] = lang
return render.index() if __name__ == "__main__": app.run()

模板文件: templates/index.html.

$_('Hello')

不要忘记生成必要的 po&mo 语言文件。参考: 模板语言中的i18n支持

现在运行 code.py:

$ python code.py
http://0.0.0.0:8080/

然后用你喜欢的浏览器访问下面的地址,检查语言是否改变:

http://your_server:8080/
http://your_server:8080/?lang=en_US
http://your_server:8080/?lang=zh_CN

你必须:

  • 确保语言文件(en_US、zh_CN等)可以动态改变。
  • 确保custom_gettext()调用越省资源约好。

参考:

web.py框架之高级应用的更多相关文章

  1. 【Python】【Web.py】详细解读Python的web.py框架下的application.py模块

    详细解读Python的web.py框架下的application.py模块   这篇文章主要介绍了Python的web.py框架下的application.py模块,作者深入分析了web.py的源码, ...

  2. web.py框架入门

    在使用微信搭建公众平台的时候,使用的是web.py这个方便简单的框架,学习一下. 框架文档:http://webpy.org/docs/0.3/tutorial.zh-cn  按照文档的内容写一遍程序 ...

  3. Web.py 框架学习笔记 - URL处理

    最近由于工作需要开始学习基于python的web应用框架web.py.为了方便学习,将学习心得逐日记下以便日后复习. URL 模板: web.py提供了一套url处理的模板,在python工程中,只需 ...

  4. web.py框架之i18n支持

    问题: 在web.py的模板文件中, 如何得到i18n的支持? Solution: 项目目录结构: proj/ |- code.py |- i18n/ |- messages.po |- en_US/ ...

  5. web.py框架之基本应用

    一.基本应用 1.1 Hello World! # coding:utf-8 import web urls = ( "/.*", "Tk_online" ) ...

  6. Web.py 框架学习笔记 - ctx

    摘要: ctx用于存取web请求的环境变量,基于ThreadedDict类进行实例化.ThreadedDict类可实例化字典类型的对象,该对象某些属性可用于存取处理线程的id. 这样字典化实例的线程池 ...

  7. (转)Nginx + uwsgi + web.py + MySQLdb

    一.背景知识: Web服务器上有多种方式运行Python: 传统上的CGI方式:基本已被淘汰 mod_python的模块方式:仍在广泛使用,但生产环境中也被逐步淘汰 uwsgi方式:目前生产环境下最受 ...

  8. python web.py操作mysql数据库,实现对数据库的增删改查操作

    使用web.py框架,实现对mysql数据库的增删改查操作: 该示例代码中连接的是本地数据库testdb,user表,表结构比较简单,只有两个字段:mobile和passwd,类型均为字符型 实际应用 ...

  9. python web.py实现简单的get和post请求

    使用web.py框架,实现简单的get和post请求: py文件名:mytest.py import web urls = ( '/', 'hello' ) app = web.application ...

随机推荐

  1. IPAddress.Any 解决本地ip和服务器ip切换问题

    IPAddress.Any表示本机ip,换言之,如果服务器绑定此地址,则表示侦听本机所有ip对应的那个端口(本机可能有多个ip或只有一个ip)IPAddress.Any微软给出的解释是:Provide ...

  2. 对Unity的Resources目录进行改名

    项目用的是Unity5.5版本,开发的时候将相关的图集.预制对象资源都放在 Resources 目录下,而真机使用的是 StreamingAssets 目录下的资源. Resources(不分层级)在 ...

  3. HIVE开发总结

    基本数据类型 查看所有函数 搜索函数 搜索表 查看函数使用方法 关键字补全 显示表头 SET环境变量 查看建表语句.数据文件置 执行外部命令 NVL CONCAT IF CASE TRIM SUBST ...

  4. jquery 多级联动下拉列表含(数据模型)

    方法 /** * 级联 * 联动 * @param url:访问json数据的地址 * @param param:参数 * @param levelIds:页面下拉标签数组,为联动级数 * @priv ...

  5. phpt5支付宝登陆支付接口解析

    先看效果图 下面的源码来源网络,自己对照修改. 放入一个插件库中,方便管理 创建支付类 1.发起支付 public function init() { $order_id = $_REQUEST['o ...

  6. EditPlus 自用正则替换

    分享下自己用EditPlus 的一些些正则技巧,editplus版本v3.5.1 1.替换a标签链接地址为空 例如: 把所有的 1 <a href="..囧.."> 替 ...

  7. 全栈JavaScript之路(十九)HTML5 插入 html标记 ( 一 )innerHTML 与outerHTML

    在须要给文档插入大量的html 标记下.通过DOM操作非常麻烦,你不仅要创建一系列的节点,并且还要小心地依照顺序把它们接结起来. 利用html 标签 插入技术,能够直接插入html代码字符串,简单.高 ...

  8. Python学习笔录

    参考:http://www.runoob.com/python3/python3-data-type.html 1. type和isinstance区别type(A()) == A, type()不会 ...

  9. C#中IEnumerable、ICollection、IList、List之间的区别

    IEnumerable.ICollection.IList.List之间的区别,本文分别分析了它的实现源码,从而总结出了它们之间的关系和不同之处. 首先我看看 IEnumerable: // 摘要: ...

  10. Centos 7.x nginx隐藏版本号

    一.打开配置文件 #vim /etc/nginx/nginx.conf 二.增加一行: server_tokens    off; 三.重启nginx #nginx -s reload 四.效果