0.1. 快速概览
1. 开始
1.1. 应用程序对象
2. 路由
2.1. 动态路由
2.2. HTTP 请求方法
2.2.1. 自动回滚
2.3. 路由静态文件
2.4. 错误页面
3. 生成内容
3.0.1. 改变默认编码
3.1. 静态文件
3.2. HTTP 错误和重定向
3.2.1. 其它异常
3.3. Response 对象
3.3.1. 状态码
3.3.2. 响应头信息
3.4. Cookies
3.4.1. 安全的 Cookies
4. 访问请求数据
4.1. HTTP 头信息
4.2. Cookies
4.3. 查询字符串
4.4. POST 表单数据和文件上传
4.5. WSGI 环境
5. 模板
5.1. 语法
5.2. 缓存
6. 开发
6.1. 调试模式
6.2. 自动重载
7. 部署
7.1. 多线程的服务器
7.2. 多个服务器进程
7.3. 使用 WSGI 和中间件
7.4. Apache mod_wsgi
7.5. Google AppEngine
7.6. 很好的旧 CGI
8. 词汇表
该教程向你介绍了 Bottle Web 框架的概念和特征。如果你有这里没有答案的问题,请查看“常见问题解答”页面,根据问题进行追踪,或发送邮件给 bottlepy@googlegroups.com。
注意:这是一个从旧文档复制&粘贴来的,并且工作在不断进展。请小心处理:)
0.1. 快速概览
路由:Web 开发开始于绑定 URLs 到代码。该章节告诉你如何完成它。
生成内容:你可以返回一些东西到浏览器。Bottle 让它变得简单,并且支持的不仅仅是普通的字符串。
访问请求数据:每个客户端请求都携带很多信息。HTTP 头信息、表单数据和 cookies。这里是如何使用它们。
模板:你不会想把 HTML 写到你的 Python 代码中,你会吗?从演示你可以看到模板分隔代码。
开发:这些工具和特征将在你的开发过程中帮助你。
部署:让它运行。
1. 开始
Bottle 没有任何依赖,因此你只需要 Python(2.5 以上到 3.x 能够很好地工作)和 bottle 模块文件。让我们开始于一个非常基础的“你好世界”例子:
from bottle import route, run# ①
@route('/hello')# ②
def hello():# ③
return '你好世界!'# ④
run(host = 'localhost', port = 8080)# ⑤
这会发生什么呢?
① 首先,我们导入一些 bottle 组件。route() 修饰器和 run() 函数。
② route() 修饰器被用来绑定一块代码到一个 URL。在这个例子中,我们想要回答来自 /hello URL 的请求。
③ 该函数是处理函数或是对 /hello 路由进行回调。每次对 /hello URL 的请求都会调用它,并且响应生成页面内容。
④ 在这个例子中,我们简单地返回一个字符串到浏览器。
⑤ 现在,是启动实际的 HTTP 服务器的时候了。默认是一个开发服务器运行在“localhost”的 8080 端口,并且在你点击 Control+C 之前一直会服务请求。
就是这样。运行该脚本,访问 http://localhost:8080/hello,你将在浏览器中看到“你好世界”。当然,这是一个非常简单的例子,但它展示了如何使用 bottle 来建立应用程序的基本概念。
1.1. 应用程序对象
几个函数和修饰器如 route() 或 run() 依靠一个全局应用程序对象来存储路由、回调和配置。这让写一个小的应用程序变得简单,但在更多复杂情景中会导致问题。如果你喜欢一个更加明确的方式来定义你的应用程序,并且不介意额外的输入,你可以创建你自己的隐含应用程序对象,并使用以替代全局那个:
from bottle import Bottle, run
myapp = Bottle()
@myapp.route('/hello')
def hello():
return '你好世界!'
run(app = myapp, host = 'localhost', port = 8080)
本手册使用全局应用程序语法是为了简单明了。但是记住,你有一个选择。面向对象的方法将在“教程-应用程序对象”章节里进行描述。
2. 路由
就像你之前学到的那样,路由被用来映射 URLs 到回调函数。这些函数在每个请求匹配的路由时被执行,并且它们的值被返回到浏览器。你可以使用 route() 修饰器为一个回调添加任意数量的路由。
from bottle import route
@route('/')
@route('/index.html')
def index():
return '〈a href="/hello"〉转到“你好世界”页面〈/a〉'
@route('/hello')
def hello():
return '你好世界!'
就像你所看到的,URLs 和路由不会对 Web 服务器上的实际文件做任何事情。路由名称对于你的回调是唯一的,仅此而已。所有没有被路由覆盖的 URLs 将回答一个“404 页面没有被找到”错误页面。
2.1. 动态路由
Bottle 有一个特别的语法来为一个路由添加通配符,并且允许一个单一的路由匹配一个宽范围的 URLs。这些动态路由通常被用在 blogs 或 wikis 以创建好看和有意义的 URLs,如 /archive/2010/04/21 或 /wiki/Page_Title。为什么?因为 URIs 是不会改变的。让我们为最后的例子添加一个 :name 通配符:
@route('/hello/:name')
def hello(name)
return '你好 %s' % name
该动态路由将匹配 /hello/alice 以及 /hello/bob。每个 URL 的片段都由一个通配符覆盖,并将通配符作为一个关键字参数传递给回调函数,因此你可以在你的应用程序中使用该信息。
正常的通配符匹配下一个斜线后面的所有东西。你可以添加一个正则表达式来改变它:
@route('/object/:id#[0-9]+#')
def view_object(id):
return '对象 ID: %d' % int(id)
正如你所见到的,关键字参数包含一个字符串,即使如果通配符被配置为只匹配数字。如果你需要,你必须明确地将它转换为整数。
2.2. HTTP 请求方法
HTTP 协议为不同的任务定义了几个请求方法(有时也被称为“动词”)。在没有指定其它方法的时候,GET 是所有路由默认的方法。这些路由只会匹配 GET 请求。要处理其它如 POST、PUT 或 DELETE 方法,你可以给 route() 修饰器添加一个 method 关键字参数,或者使用下面的 4 个修饰器之一:get()、post()、put() 或 delete()。
POST 方法通常被用在 HTML 表单提交。该例子展示了如何使用 POST 来处理一个登录表单:
from bottle import get, post, request
# @route('/login')
@get('/login')
def login_form():
return '''
〈form method="POST"〉
〈input type="text" name="name" /〉
〈input type="password" name="password" /〉
〈/form〉
'''
# @route('/login', method="POST")
@post('/login')
def login_submit():
name = request.forms.get('name')
password = request.forms.get('password')
if check_login(name, password):
return '〈p〉你已正确登录〈/p〉'
else:
return '〈p〉登录失败〈/p〉'
在该例中,/login URL 被绑定给两个不同的回调,一个是 GET 请求,另一个是 POST 请求。首先,显示一个 HTML 表单给用户。其次,在一个表单提交后调用回调函数,并且检查由用户输入到表单的登录凭证。Request.forms 的用法将在“访问请求数据”章节进一步描述。
2.2.1. 自动回滚
特别的 HEAD 方法被用来要求响应一个与之对应相同的 GET 请求,但是没有响应主体。这对于检索一个不需要下载整个文件的资源的元信息是很有用的。在当前,Bottle 通过回滚到相应的 GET 路由并截掉请求主体,来自动地处理这些请求。你自己不用指定任何 HEAD 路由。
另外,非标准的 ANY 方法作为一个低优先级的回滚进行工作:监听 ANY 的路由,只有在没有其它更具体的路由被定义时,才匹配那些无论其 HTTP 方法的请求。这对 proxy-routes(重定向请求到更多具体的子应用程序)是很有用的。
概括起来:只要对于原始请求方法没有其匹配的路由,HEAD 请求回滚到 GET 路由,并且所有请求回滚到 ANY 路由。它就是这么简单。
2.3. 路由静态文件
静态文件如图片或 CSS 文件不会被自动地传送。你必须添加一个路由和一个回调函数来控制哪些文件可以被传送和在哪儿可以找到它们:
from bottle import static_file
@route('/static/:filename')
def server_static(filename):
return static_file(filename, root = '/path/to/your/static/files')
static_file() 函数对于以一种安全和方便的方式来服务文件是有帮助的(请看静态文件)。该示例直接将文件限制在 /path/to/your/static/files 目录下,因为 :filename 通配符不会匹配一个带有斜线的路径。要同时在子目录中服务文件,我们可以放松一点通配符:
@route('/static/:path#.+#')
def server_static(path):
return static_file(path, root = '/path/to/your/static/files')
当指定一个相对的根路径(如 root = './static/files')时要小心。工作目录(./)和项目目录不允许相同。
2.4. 错误页面
如果有什么错误,Bottle 将显示一个信息,但却是一个相当乏味的错误页面。你可以使用 error() 修饰器来覆盖默认的错误页面。它工作起来与 route() 修饰器类似,但除了一个 HTTP 状态码替代了一个路由:
@error(404)
def error404(error):
return '这里没有任何东西,对不起'
error 参数传递一个错误处理程序(它是一个 HTTPError 的实例)。
3. 生成内容
在纯的 WSGI 中,从你的应用程序里返回的类型的范围是非常受限的。应用程序必须返回一个可迭代的有伸缩性的字节字符串。你可以返回一个字符串(因为字符串是可迭代的),但这会导致大多数服务器一个字符一个字符地传送你的内容。Unicode 字符也是不允许的。这是非常不切合实际的。
Bottle 要更加灵活些,并且支持一个比较宽泛的类型范围。如果可能的话,它甚至会添加一个 Content-Type 头信息,并且自动地编码 Unicode,因此你不必那么做。以下是一个数据类型的列表,可以从你的应用程序回调来返回,并且有一个如何通过框架来处理这些东西的简短描述:
字典
如上所述,Python 字典(或其子类)会自动地转换为 JSON 字符串,并将带有设置为 application/json 的 Content-Type 头信息返回给浏览器。这让它很容易去实现基于 JSON 的 APIs。JSON 以外的其它数据格式也是被支持的。请看“教程-输出过滤器”以学习更多内容。
空字符串、False、None 或其它非 True 的值
这些会产生一个带有值为 0 的 Content-Length 头信息的空输出。
Unicode 字符串
Unicode 字符串(或可迭代的有伸缩性的 Unicode 字符串)可以通过编码器在 Content-Type 头信息中所指定的(默认为 utf8)被自动地编码,然后作为普通的字节字符串(请看下面内容)。
字节字符串
Bottle 作为一个整体来返回字符串(而不是迭代每一个字符)并基于字符串长度添加一个 Content-Length 头信息。字节字符串列表是首先被加入的。其它可迭代的有伸缩性的字节字符串没有被加入,因为它们可能会变得太大而不能装入到内存中。在这种情况下,Content-Length 头信息是不会被设置的。
HTTPError 或 HTTPResponse 的实例
当将它们作为一个异常抛出时,返回它们时拥有相同的作用。请看“教程-错误处理”以获得详情。
文件对象
每个东西都有一个 .read() 方法,被视为一个文件或类似文件的对象,并且传递给由 WSGI 服务器框架所定义的可调用的 wsgi.file_wrapper。一些 WSGI 服务器的实现,能够利用优化的系统调用(sendfile)来更有效地传输文件。在其它情况下,这仅仅是遍历填充在内存中的每个块。可选的头信息如 Content-Type 或 Content-Length 都不会被自动设置。如果可能,使用 send_file()。请看“静态文件”以获得详情。
可迭代和生成器
你被允许在你的回调或返回一个可迭代的时利用产出,只要可迭代的产生了字节字符串、Unicode 字符串、HTTPError 或 HTTPRequest 实例。但是,嵌套迭代是不允许的。请注意,一旦 HTTP 状态码和头信息被发送到浏览器,第一个非空值就是可迭代的产出。在稍后改变它不会有任何影响。
该列表的顺序是有意义的。例如,你可以返回一个带有 read() 方法的 str 的子类。它依然被认为是一个字符串而不是一个文件,因为字符串是最先被处理的。
3.0.1. 改变默认编码
Bottle 使用 Content-Type 头信息的 charset 参数来决定如何编码 Unicode 字符串。该头信息默认为 text/html;charset=UTF8,并且可以通过使用 Response.content_type 属性或设置 Response.charset 属性来直接地修改。(Response 对象将在“Response 对象”章节中进行描述。)
from bottle import response
@route('/iso')
def get_iso():
response.charset = 'ISO-8859-15'
return u'这将被带有 ISO-8859-15 的编码发送。'
@route('/latin9')
def get_latin():
response.content_type = 'text/html;charset=latin9'
return u'ISO-8859-15 也被称为 latin9。'
在一些罕见的情况下,Python 编码名称与 HTTP 特征所支持的名称不同。那么,你必须做到以下两点:首先设置 Response.content_type 头信息(不变地发送到客户端),然后设置 Response.charset 属性(被用来编码 Unicode)。
3.1. 静态文件
你可以直接地返回文件对象,但 static_file() 才是被推荐的服务静态文件的方法。它可以自动地猜测一个 MIME 类型,添加一个 Last-Modified 头信息,由于安全因素将路径限制在一个根目录下,和生成合适的错误相应(401 的权限错误、404 的文件丢失)。它甚至支持 If-Modified-Since 头信息和最终生成一个 304 未修改响应。你可以传递一个自定义的 MIME 类型来禁用 MIME 类型的猜测。
from bottle import static_file
@route('/images/:filename#.*.png#')
def send_image(filename):
return static_file(filename, root = '/path/to/image/files', mimetype = 'image/png')
@route('/static/:filename')
def send_static(filename):
return static_file(filename, root = '/path/to/static/files')
如果你真的需要,你可以将 static_file() 的返回值作为一个异常来抛出。
3.1.1. 强制下载
如果 MIME 类型是已知的,并且被分配给一个应用程序(例如:PDF 文件),大多数浏览器会尝试打开已下载的文件。如果这不是你想要的东西,你可以为用户强制一个下载对话框,甚至是建议一个文件名:
@route('/download/:filename')
def download(filename):
return static_file(filename, root = '/path/to/static/files', download = filename)
如果 download 参数仅是 True,则将使用原始的文件名。
3.2. HTTP 错误和重定向
abort() 函数是生成 HTTP 错误页面的一个捷径。
from bottle import route, abort
@route('/restricted')
def restricted():
abort(401, "对不起,访问拒绝。")
要重定向一个客户端到一个不同的 URL,你可以发送一个 303 See Other 响应,其中的 Location 头信息被设置为新的 URL。redirect() 可以为你做到:
from bottle import redirect
@route('/wrong/url')
def wrong():
redirect("/right/url")
你可以提供一个不同的 HTTP 状态码来作为第二个参数。
注意:这两个函数都将通过抛出一个 HTTPError 异常来中断你的回调代码。
3.2.1. 其它异常
HTTPResponse 和 HTTPError 以外的其它所有异常都将导致一个 500 内部服务器错误响应,因此它们将不会搞垮你的 WSGI 服务器。你可以关掉这个行为(通过设置 bottle.app().catchall 为 False),以便在你的中间件中处理异常。
3.3. Response 对象
响应的元数据如 HTTP 状态码、响应的头信息和 cookies 都被存储在一个叫做 Response 的对象中,以便将它们传送到浏览器。你可以直接地操纵这些元数据,或使用预定义的帮助器方法来操纵这些。完整的 API 和特征列表被描述在 API 章节(请看“Response”),但最常见的用例和特征都涵盖在这里。
3.3.1. 状态码
HTTP 状态码控制浏览器的行为,并且默认为 200 OK。在大多数情况下,你不会需要手动地设置 Response.status 属性,除了使用 abort() 帮助器或返回一个带有适当状态码的 HTTPResponse 实例的时候。任何整数都是被允许的,除了定义在 HTTP 特征中的代码,因为它将给浏览器带来混乱的影响,并且会破坏标准。
3.3.2. 响应头信息
添加值到 Response.headers 字典,以添加或改变响应头信息。注意,键是不区分大小写的。
@route('/wiki/:page')
def wiki(page):
response.headers['Content-Language'] = 'en'
return get_wiki_page(page)
3.4. Cookies
...
3.4.1. 安全的 Cookies
...
4. 访问请求数据
Bottle 提供一个全局的请求对象来访问 HTTP 元数据如 cookies、头信息和 POST 表单数据。只要它是从一个回调函数中被访问,该对象总是包含有关 current 请求的信息。该工作甚至可以在一个多线程的环境中,可以在同一时间处理多个请求。一个全局对象如何作为线程安全的详细信息,请看上下文。
注意:Bottle 在 MultiDict 实例中存储了大部分解析过的 HTTP 元数据。这些行为与普通的字典相似,但能够为每个键存储多个值。标准的字典访问方法将只返回一个单一值。使用 MultiDict.getall() 方法可以收到一个指定键的所有值的列表(可能为空)。HeaderDict 类继承自 MultiDict,另外,使用不区分大小写的键。
完整的 API 和特征列表被描述在 API 章节(请看“Request”),但最常见的用例和特征都涵盖在这里。
4.1. HTTP 头信息
头信息都被存储在 Request.header 中。属性是一个 HeaderDict 实例,它基本上是一个不区分大小写键的字典:
from bottle import route, request
@route('/is_ajax')
def is_ajax():
if request.header.get('X-Requested-With') == 'XMLHttpRequest':
return '这是一个 AJAX 请求'
else:
return '这是一个普通请求'
4.2. Cookies
Cookies 作为一个普通的字典被存储在 Request.COOKIES 中。Request.get_cookie() 方法允许访问 Cookies,将在下一个单独的章节进行描述。该例展示了一个简单的基于 cookie 的显示计数:
from bottle import route, request, response
@route('/counter')
def counter():
count = int(request.COOKIES.get('counter', '0'))
count += 1
response.set_cookie('counter', str(count))
return '你已访问该页面 %d 次' % count
4.3. 查询字符串
查询字符串(如 /forum?id=1&page=5)通常用来传送一个小量的键/值对到服务器。你可以使用 Request.GET 字典来访问它们的值,并且通过 Request.query_string 属性可以获得整个字符串。
from bottle import route, request, response
@route('/forum')
def display_forum():
forum_id = request.GET.get('id')
page = request.GET.get('page', '1')
return '论坛 ID: %s (页码 %s)' % (forum_id, page)
4.4. POST 表单数据和文件上传
POST 和 PUT 请求方式的请求主体包含以各自格式编码的表单数据。使用 Request.forms 属性(一个 MultiDict)来访问普通的 POST 表单字段。文件上传作为 cgi.FieldStorage 实例被分别存储在 Request.files 中。Request.body 属性持有一个带有原始主体数据的文件对象。
这是一个简单文件上传表单的例子:
〈form action="/upload" method="post" enctype="multipart/form-data"〉
〈input type="text" name="name" /〉
〈input type="file" name="data" /〉
〈/form〉
from bottle import route, request
@route('/upload', method = 'POST')
def do_upload():
name = request.forms.get('name')
data = request.files.get('data')
if name and data:
raw = data.file.read() # 这对于大文件来说是很危险的
filename = data.filename
return "你好 %s! 你的上传 %s (%d 字节)." % (name, filename, len(raw))
return "你丢失了一个字段。"
4.5. WSGI 环境
Request 对象在 Request.environ 存储 WSGI 环境字典,并且允许以类似字典的方式来访问其值。请看“WSGI 特征”以获得更多信息。
@route('/my_ip')
def show_ip():
ip = request.environ.get('REMOTE_ADDR')
# 或 ip = request.get('REMOTE_ADDR')
# 或 ip = request['REMOTE_ADDR']
return "你的 IP 是: %s" % ip
5. 模板
Bottle 带有一个快速和强有力的内建模板引擎,叫做“SimpleTemplate”引擎。要渲染一个模板,你可以使用 template() 函数或 view() 修饰器。你所必须要做的事情是,提供模板的名称和将变量作为关键字参数传递给模板。这里有一个简单的例子,显示了如何渲染一个模板:
@route('/hello')
@route('/hello/:name')
def hello(name = 'World'):
return template('hello_template', name = name)
这将加载模板文件 hello_template.tpl 并使用名称变量集渲染它。Bottle 将在 ./views/ 目录或由 bottle.TEMPLATE_PATH 列表所指定的目录中进行查找。
view() 修饰器允许你返回一个带有模板变量的字典以替代调用 template():
@route('/hello')
@route('/hello/:name')
@view('hello_template')
def hello(name = 'World'):
return dict(name = name)
5.1. 语法
该模板语法是围绕 Python 语言的一个薄层。它的主要目的是确保正确的块缩进,因此你可以格式化你的模板,而不需要担心缩进。下面的链接是一个完整的语法描述:SimpleTemplate 模板。
这是一个模板示例:
%if name == '世界':
〈h1〉你好{{name}}!〈/h1〉
〈p〉这是一个测试。〈/p〉
%else:
〈h1〉你好{{name.title()}}!〈/h1〉
〈p〉你好吗?〈/p〉
%end
5.2. 缓存
模板在编译后被缓存在内存中。修改模板文件将不会有任何影响,除非你清除模板缓存。调用 bottle.TEMPLATES.clear() 可以做到。在调试模式,缓存可以被禁用。
6. 开发
在开发过程中,Bottle 有两个特征是很有用的。
6.1. 调试模式
在调试模式,bottle 将更加详细,并且尝试帮助你找到错误。在产生环境,你应该从不使用调试模式。
import bottle
bottle.debug(True)
这会做下面的事:
异常将打印堆栈跟踪。
错误页面将包含堆栈跟踪。
模板将不会被缓存。
6.2. 自动重载
在开发过程中,你需要重启服务器很多次来检测你当前的改变。自动重载器能够为你做到这些。当每次你编辑一个模块文件时,重载器重启服务器进程,并重载你代码的最新版本。
from bottle import run
run(reloader = True)
它是如何工作的:主进程将不会启动一个服务器,但会引发一个子进程,使用相同的命令行参数来启用主进程。所有模块级的代码最后将被执行两次!小心。
子进程将拥有一个设置为 True 的 os.environ['BOTTLE_CHILD'],并且启用一个普通的非重载的应用程序服务器。一旦任何已加载的模块发生改变,子进程将被终止,并由主进程重新引发。模块文件中的改变将不会触发一个重载。请使用调试模式以停用模板缓存。
重载取决于是否有能力停止子进程。如果你运行在 Windows 或任何其它不支持 signal.SIGINT(在 Python 中抛出 KeyboardInterrupt)的操作系统上,signal.SIGTERM 将被用来杀掉子进程。注意,退出处理程序和 finally 子句等等,在一个 SIGTERM 之后都不会被执行。
7. 部署
Bottle 默认使用内建的 wsgiref.SimpleServer。这个非线程的 HTTP 服务器对于开发和早期的生产是非常完美的,但当服务器负载过多时,可能会变成一个性能瓶颈。
这有三种方式来消除该瓶颈:
使用一个多线程的服务器适配器。
在多个 bottle 实例之间分散负载。
以上两者全做。
7.1. 多线程的服务器
最简单增加性能方式的方式是,安装一个多线程的并具有 WSGI 能力的 HTTP 服务器,就像 Paste、flup、cherrypy 或 fapws3,并使用相应的 bottle 服务器适配器。
from bottle import PasteServer, FlupServer, FapwsServer, CherryPyServer
bottle.run(server = PasteServer) # 示例
如果在你喜欢的服务器上缺少 bottle 适配器,或者你想要调整服务器的设置,你可能需要手动地设置你的 HTTP 服务器并使用 bottle.default_app() 来访问你的 WSGI 应用程序。
def run_custom_paste_server(self, host, port):
myapp = bottle.default_app()
from paste import httpserver
httpserver.serve(myapp, host = host, port = port)
7.2. 多个服务器进程
一个 Python 进程一次只能利用一个 CPU,即使有多个可用的 CPU 核心。诀窍是在多个独立的 Python 进程之间均衡负载以利用所有的 CPU 核心。
你可以使用不同的本地端口(localhost:8080、localhost:8081、localhost:8082、...),为每一个可用的 CPU 启动一个服务器实例,而不是一个单一的 Bottle 应用程序服务器。然后,一个高性能的负载均衡器充当一个反向代理,并将每个新的请求发送到一个随机的 Bottle 进程,在所有可用的后端服务器实例之间分散负载。该方式可以让你使用所有的 CPU 核心,甚至是在不同的物理服务器上分散负载。
但这会有几个缺点:
在多个 Python 进程之间,你不能简单地共享数据。
在相同的时间里,它会花销掉很多内存来运行 Python 和 Bottle 的几个副本。
可用的最快的负载均衡器之一是 Pound,但大多数普通的 Web 服务器拥有一个代理模块,它也可以很好地工作。
很快,我将为 lighttpd 和 Apache Web 服务器添加示例。
7.3. 使用 WSGI 和中间件
调用 bottle.default_app() 将返回你的 WSGI 应用程序。在应用你喜欢的那些 WSGI 中间件模块之后,你可以告诉 bottle.run() 去使用你封装的应用程序,而不是默认的那个。
from bottle import default_app, run
app = default_app()
newapp = YourMiddleware(app)
run(app = newapp)
Bottle 创建一个 bottle.Bottle() 的单一实例,并将它作为大多数模块级修饰器和 bottle.run() 程序的一个默认。bottle.default_app() 返回(或改变)该默认。然而,你可以创建你自己的 bottle.Bottle() 实例。
from bottle import Bottle, run
mybottle = Bottle()
@mybottle.route('/')
def index():
return 'default_app'
run(app = mybottle)
7.4. Apache mod_wsgi
你可以附加 Bottle 应用程序到一个使用 mod_wsgi 和 Bottle WSGI 接口的 Apache 服务器,而不是从 Bottle 中运行你自己的 HTTP 服务器。
所有你需要的是一个 app.wsgi 文件,它提供了一个应用程序对象。该对象被 mod_wsgi 用来启动你的应用程序,并作为一个 WSGI 兼容的 Python 调用。
文件 /var/www/yourapp/app.wsgi:
# 改变工作目录,以便在相对路径(和模板查找)中继续工作
os.chdir(os.path.dirname(__file__))
import bottle
# ... 在这里添加或导入你的 bottle 应用程序代码 ...
# 不要在 mod_wsgi 中使用 bottle.run()
application = bottle.default_app()
Apache 的配置看起来像这样:
〈VirtualHost *〉
ServerName example.com
WSGIDaemonProcess yourapp user=www-data group=www-data processes=1 threads=5
WSGIScriptAlias / /var/www/yourapp/app.wsgi
〈Directory /var/www/yourapp〉
WSGIProcessGroup yourapp
WSGIApplicationGroup %{GLOBAL}
Order deny, allow
Allow from all
〈/Directory〉
〈/VirtualHost〉
7.5. Google AppEngine
我自己没有测试它,但几个 Bottle 用户报告这会工作得很好:
import bottle
from google.appengine.ext.webapp import util
# ... 在这里添加或导入你的 bottle 应用程序代码 ...
# 不要在 AppEngine 中使用 bottle.run()
util.run_wsgi_app(bottle.default_app())
7.6. 很好的旧 CGI
CGI 缓慢得如地狱,但它可以工作:
import bottle
# ... 在这里添加或导入你的 bottle 应用程序代码 ...
bottle.run(server = bottle.CGIServer)
8. 词汇表
回调
当一些外部的动作发生时,程序员代码将被调用。在 Web 框架的上下文中,URL 路径和应用程序代码之间的映射常常通过为每个 URL 指定一个回调函数来实现。
修饰器
一个函数返回其它函数,通常作为一个使用“@修饰器”语法的函数转型来应用。请看“Python 有关函数定义的文档”以获得更多有关修饰器的信息。
环境
保存根目录下所有文档信息的一个结构,用以相互参照。在解析阶段之后环境将被腌制,以便连续地运行只需要读取和解析新的和已改变的文档。
处理函数
一个处理一些特定事件或情况的函数。在 Web 框架中,应用程序通过为每一个 URL 包括应用程序附加一个作为回调的处理程序来进行开发。
安全 cookie
Bottle 创建带有对象的签名 cookie 该对象能被腌制。当用在 request.set_cookie() 中的值不是一个字符串类型时,一个安全 cookie 将被自动地创建,并且 bottle 的配置包括一个带有一个盐的 securecookie.key 条目。
源目录
对于一个 Sphinx 项目,该目录包括它的子目录、包含所有源码文件。