用户指南¶
本指南介绍 Bottle Web 框架的概念和功能,涵盖基础和高级主题。你可以从头到尾阅读,也可以稍后将其用作参考。自动生成的 API 参考 可能也对你有所帮助。它涵盖更多细节,但解释不如本教程详细。常见问题的解答可在我们的 常见问题解答 (F.A.Q.) 集合或 常见问题解答 (F.A.Q.) 页面找到。如果你需要任何帮助,请加入我们的邮件列表或访问我们的IRC 频道。
安装¶
Bottle 不依赖任何外部库。你可以直接将 bottle.py 下载到你的项目目录中并开始编码
$ wget https://bottlepy.cn/bottle.py
这将获取包含所有新功能的最新开发快照。如果你更喜欢稳定的环境,应使用稳定版本。这些版本可在 PyPI 上获取,并且可以通过 pip(推荐)或你的包管理器安装
$ pip install --user bottle # better use a venv, see below
$ sudo apt-get install python-bottle # works for debian, ubuntu, ...
通常更好的做法是为每个项目创建一个 virtualenv 并使用它来安装包
$ python3 -m venv venv # Create virtual environment
$ source venv/bin/activate # Change default python to virtual one
(venv)$ pip install -U bottle # Install bottle to virtual environment
Hello World!¶
本教程假设你已将 Bottle 安装或复制到你的项目目录中。让我们从一个非常基本的“Hello World”示例开始
from bottle import route, run
@route('/hello')
def hello():
return "Hello World!"
if __name__ == '__main__':
run(host='localhost', port=8080, debug=True)
就是这样。运行此脚本,访问 http://localhost:8080/hello,你将在浏览器中看到“Hello World!”。下面是它的工作原理
route()
装饰器将一段代码绑定到 URL 路径。在本例中,我们将 /hello
路径链接到 hello()
函数。这被称为路由(因此得名装饰器),是本框架最重要的概念。你可以定义任意数量的路由。每当浏览器请求一个 URL 时,相应的函数就会被调用,并且返回值会被发送回浏览器。就是这么简单。
最后一行中的 run()
调用会启动一个内置的开发服务器。它运行在 localhost
的 8080
端口,并处理请求直到你按下 Control-c。你稍后可以切换服务器后端(参见 部署),但目前我们只需要一个开发服务器。它完全不需要设置,是一种非常轻松的方式,让你的应用程序可以启动并运行进行本地测试。
调试模式在早期开发阶段非常有用,但在面向公众的应用程序中应关闭。请记住这一点。
这只是演示了使用 Bottle 构建应用程序的基本概念。继续阅读,你将看到更多可能性。
应用对象¶
为了简化,本教程中的大多数示例使用模块级别的 route()
和其他装饰器来定义路由。这些引用一个全局的“默认应用”,它是 Bottle
的一个实例,在你第一次调用 route()
或其相关函数时自动创建。如果你更喜欢更明确的方法并且不介意额外输入一些代码,你可以创建一个单独的应用对象并使用它,而不是全局的应用对象
from bottle import Bottle, run
app = Bottle()
@app.route('/hello')
def hello():
return "Hello World!"
if __name__ == '__main__':
app.run(host='localhost', port=8080)
面向对象的方法在 应用结构 一节中有进一步描述。只需记住你有一个选择。
调试模式¶
在早期开发阶段,调试模式非常有用。
# Enable debug at runtime
bottle.debug(True)
# or during startup
run(..., debug=True)
在此模式下,Bottle 会输出更多信息,并在发生错误时提供有用的调试信息。它还会禁用一些可能妨碍你的优化,并添加一些检查来警告你可能的配置错误。以下是调试模式下变化的一些事项的不完整列表
默认错误页面显示堆栈跟踪。
模板不会被缓存。
插件立即生效。
请确保不在生产服务器上使用调试模式。
自动重载¶
厌倦了每次更改代码后都重新启动服务器吗?自动重载器可以为你完成这项工作。每次你编辑模块文件时,重载器都会重新启动服务器进程并加载最新版本的代码。
run(..., debug=True, reloader=True)
工作原理:主进程不会启动服务器,而是使用与启动主进程相同的命令行参数生成一个新的子进程。所有模块级代码至少执行两次!请务必小心。
子进程的 os.environ['BOTTLE_CHILD']
将被设置为 True
,并作为一个正常的非重载应用服务器启动。一旦任何已加载的模块发生变化,子进程就会终止,并由主进程重新生成。模板文件的更改不会触发重载。请使用调试模式来禁用模板缓存。
命令行接口¶
Bottle 不仅是一个模块,也是一个命令行可执行文件,可以用来启动你的应用,而不是通过编程方式调用 run()
。如果你通过 pip 或类似工具安装了 Bottle,你的路径中也会有一个方便的 bottle 命令。尝试以下其中一种方式
bottle --help
python3 -m bottle --help
./path/to/bottle.py --help
下面是一个快速示例
$ bottle --debug --reload mymodule
Bottle v0.13-dev server starting up (using WSGIRefServer())...
Listening on http://localhost:8080/
Hit Ctrl-C to quit.
0.13 版更改: 安装到(虚拟)环境中的可执行脚本曾命名为 bottle.py
,这可能导致循环导入。旧名称现已弃用,新的可执行文件仅命名为 bottle
。
请求路由¶
在上一章中,我们构建了一个非常简单的 Web 应用程序,只有一个路由。这里再次展示“Hello World”示例的路由部分
@route('/hello')
def hello():
return "Hello World!"
route()
装饰器将 URL 路径链接到回调函数,并为 默认应用 添加一个新路由。然而,只有一个路由的应用有点无聊。让我们添加一些更多(别忘了 from bottle import template
)
@route('/')
@route('/hello/<name>')
def greet(name='Stranger'):
return template('Hello {{name}}, how are you?', name=name)
这个示例演示了两件事:你可以将多个路由绑定到一个回调,并且可以将通配符添加到 URL 并通过关键字参数访问它们。
动态路由¶
包含通配符的路由称为动态路由(与静态路由相对),并且同时匹配多个 URL。简单的通配符由包含在尖括号中的名称(例如 <name>
)组成,并接受一个或多个字符,直到下一个斜杠(/
)。例如,路由 /hello/<name>
接受对 /hello/alice
和 /hello/bob
的请求,但不接受对 /hello
、/hello/
或 /hello/mr/smith
的请求。
每个通配符会将 URL 中匹配的部分作为关键字参数传递给请求回调。你可以立即使用它们,轻松实现 RESTful、美观且有意义的 URL。以下是一些其他示例及其匹配的 URL
@route('/wiki/<pagename>') # matches /wiki/Learning_Python
def show_wiki_page(pagename):
...
@route('/<action>/<user>') # matches /follow/defnull
def user_api(action, user):
...
可以使用过滤器来定义更具体的通配符,和/或在将 URL 的匹配部分传递给回调之前对其进行转换。带过滤器的通配符声明为 <name:filter>
或 <name:filter:config>
。可选的配置部分的语法取决于使用的过滤器。
默认实现了以下过滤器,后续可能会添加更多
:int 仅匹配(带符号)数字,并将值转换为整数。
:float 类似于 :int,但用于十进制数。
:path 以非贪婪方式匹配所有字符,包括斜杠字符,可用于匹配多个路径段。
:re 允许你在配置字段中指定自定义正则表达式。匹配的值不会被修改。
让我们看一些实际示例
@route('/object/<id:int>')
def callback(id):
assert isinstance(id, int)
@route('/show/<name:re:[a-z]+>')
def callback(name):
assert name.isalpha()
@route('/static/<path:path>')
def callback(path):
return static_file(path, ...)
你也可以添加自己的过滤器。详情请参见 请求路由。
HTTP 请求方法¶
HTTP 协议定义了几种请求方法(有时也称为“动词”)用于不同的任务。对于未指定其他方法的路由,GET 是默认方法。这些路由将只匹配 GET 请求。要处理 POST、PUT、DELETE 或 PATCH 等其他方法,可以在 route()
装饰器中添加 method
关键字参数,或使用以下五种替代装饰器之一:get()
、post()
、put()
、delete()
或 patch()
。
POST 方法常用于 HTML 表单提交。此示例展示了如何使用 POST 处理登录表单
from bottle import get, post, request # or route
@get('/login') # or @route('/login')
def login():
return '''
<form action="/login" method="POST">
Username: <input name="username" type="text" />
Password: <input name="password" type="password" />
<input value="Login" type="submit" />
</form>
'''
@post('/login') # or @route('/login', method='POST')
def do_login():
username = request.forms.username
password = request.forms.password
if check_login(username, password):
return "<p>Your login information was correct.</p>"
else:
return "<p>Login failed.</p>"
在此示例中,/login
URL 链接到两个不同的回调,一个用于 GET 请求,另一个用于 POST 请求。第一个回调向用户显示一个 HTML 表单。第二个回调在表单提交时被调用,并检查用户在表单中输入的登录凭据。Request.forms <BaseRequest.forms> 的用法在 :ref:`tutorial-request
一节中有进一步描述。
特殊方法:HEAD 和 ANY
HEAD 方法用于请求与 GET 请求对应的响应,但不包含响应正文。这对于检索资源的元信息而无需下载整个文档非常有用。Bottle 会自动处理这些请求,通过回退到相应的 GET 路由并截断请求正文(如果存在)来实现。你无需自己指定任何 HEAD 路由。
此外,非标准的 ANY 方法作为低优先级回退:监听 ANY 的路由将匹配请求,无论其 HTTP 方法是什么,但仅在没有定义其他更具体的路由时才会匹配。这对于将请求重定向到更具体的子应用的代理路由很有帮助。
总而言之:HEAD 请求回退到 GET 路由,所有请求回退到 ANY 路由,但前提是没有针对原始请求方法的匹配路由。就这么简单。
提供静态资源¶
静态文件或资源,例如图像或 CSS 文件,不会自动提供。你必须添加一个路由和回调来控制哪些文件被提供以及在哪里找到它们。Bottle 提供了一个方便的 static_file()
函数,可以完成大部分工作,并以安全便捷的方式提供文件
from bottle import static_file
@route('/static/<filepath:path>')
def server_static(filepath):
return static_file(filepath, root='/path/to/your/static/files')
请注意,我们在这里使用了 :path
路由过滤器,以允许 filepath
中包含斜杠字符,并同时提供子目录中的文件。
static_file()
助手函数与手动处理文件相比有很多优势。最重要的是,它通过限制文件访问到指定的 root
目录来防止目录遍历攻击(例如 GET /static/../../../../etc/secrets
)。不过,请确保为 root
使用绝对路径。相对路径(以 ./
开头)会相对于当前工作目录解析,而当前工作目录可能并非总是你的项目目录。
static_file()
函数返回 HTTPResponse
或 HTTPError
,如果需要,可以将其作为异常引发。
文件下载
大多数浏览器在知道 MIME 类型的情况下(例如 PDF 文件)会尝试使用关联的应用程序打开下载的文件。如果这不是你想要的行为,你可以强制弹出下载对话框,甚至向用户建议文件名
@route('/download/<filename>')
def download(filename):
return static_file(filename, root='/path/to/static/files', download=f"download-{filename}")
如果 download
参数仅为 True
,则使用原始文件名。
生成内容¶
我们已经知道路由回调可以返回字符串作为内容,但还有更多:Bottle 支持多种其他数据类型,甚至可能添加 Content-Length
等其他头信息,这样你就不必自己操作了。以下是你可能从应用回调中返回的数据类型列表,以及框架如何处理这些数据类型的简要说明
- 字典
Python 字典(或其子类)会自动转换为 JSON 字符串并返回给浏览器,同时
Content-Type
头被设置为application/json
。这使得实现基于 JSON 的 API 变得容易,并且实际上是由JsonPlugin
实现的,该插件会自动应用于所有路由。如果需要,你可以配置和禁用 JSON 处理。- 空字符串、
False
、None
或其他非真值 这些将生成空响应。
- 字符串列表
字节或 Unicode 字符串列表会连接成一个字符串,然后像往常一样处理(见下文)。
- Unicode 字符串
Unicode 字符串会使用
Content-Type
头中指定的编码(默认为utf8
)自动编码,然后作为字节字符串处理(见下文)。- 字节字符串
原始字节字符串按原样写入响应。
HTTPError
或HTTPResponse
的实例抛出或返回
HTTPResponse
的实例将覆盖对全局request
对象所做的任何更改,然后照常继续。如果是HTTPError
,将首先应用错误处理程序。详情请参见 错误处理。- 文件或类文件对象
任何具有
.read()
方法的对象都被视为文件或类文件对象,并传递给 WSGI 服务器框架定义的wsgi.file_wrapper
可调用对象。一些 WSGI 服务器实现可以利用优化的系统调用(例如 sendfile)更高效地传输文件。在其他情况下,这只是迭代适合内存的块。可选的头信息,如Content-Length
或Content-Type
不会自动设置。出于安全和其他原因,你应该总是优先使用static_file()
,而不是直接返回原始文件。详情请参见 提供静态资源。- 可迭代对象或生成器
你可以从路由回调中
yield
字节或 Unicode 字符串(不能同时),Bottle 将以流式方式将其写入响应。在这种情况下,不会设置Content-Length
头,因为最终响应大小未知。不支持嵌套的可迭代对象,抱歉。请注意,一旦可迭代对象 yield 第一个非空值,HTTP 状态码和头信息就会发送到浏览器。之后更改这些信息无效。如果可迭代对象的第一个元素是HTTPError
或HTTPResponse
,则忽略迭代器的其余部分。
此列表的顺序很重要。例如,你可以返回一个带有 read()
方法的 str
子类。它仍然被视为字符串而不是文件,因为字符串首先被处理。
更改默认编码
Bottle 使用 Content-Type
头中的 charset 参数来决定如何编码 Unicode 字符串。该头默认为 text/html; charset=UTF8
,可以通过 Response.content_type
属性或直接设置 Response.charset
属性来更改。(Response
对象在 响应对象 一节中描述。)
from bottle import response
@route('/iso')
def get_iso():
response.charset = 'ISO-8859-15'
return u'This will be sent with ISO-8859-15 encoding.'
@route('/latin9')
def get_latin():
response.content_type = 'text/html; charset=latin9'
return u'ISO-8859-15 is also known as latin9.'
在某些极少数情况下,Python 编码名称与 HTTP 规范支持的名称不同。这时,你需要做两件事:首先设置 Response.content_type
头(它会原样发送给客户端),然后设置 Response.charset
属性(用于编码 Unicode)。
错误处理¶
如果出现问题,Bottle 会显示一个信息丰富但相当朴素的错误页面。如果启用了 debug()
模式,该页面会包含堆栈跟踪。你可以使用 error()
装饰器覆盖默认错误页面并为特定的 HTTP 状态码注册错误处理程序
from bottle import error
@error(404)
def error404(error):
return 'Nothing here, sorry'
从现在起,404
(文件未找到)错误将向用户显示一个自定义错误页面。传递给错误处理程序的唯一参数是 HTTPError
的实例。除此之外,错误处理程序与常规请求回调非常相似。你可以从 request
读取,写入 response
,并返回任何支持的数据类型,除了 HTTPError
实例。
错误处理程序仅在你应用返回或抛出 HTTPError
异常时才会使用(abort()
就实现了这一点)。将 Response.status
设置为错误代码或返回 HTTPResponse
不会触发错误处理程序。
使用 abort()
触发错误
abort()
函数是触发 HTTP 错误的快捷方式。
from bottle import route, abort
@route('/restricted')
def restricted():
abort(401, "Sorry, access denied.")
使用 abort()
时无需返回任何内容。它会引发 HTTPError
异常。
其他异常
所有 HTTPResponse
或 HTTPError
之外的异常都将导致 500 Internal Server Error
响应,因此它们不会使你的 WSGI 服务器崩溃。你可以通过将 bottle.app().catchall
设置为 False
来关闭此行为,以便在你的中间件中处理异常。
response
对象¶
响应元数据,例如 HTTP 状态码、响应头和 cookie,存储在一个名为 response
的对象中,直到它们被传输到浏览器。你可以直接操作这些元数据,或使用预定义的助手方法。完整的 API 和功能列表在 API 部分描述(参见 BaseResponse
),但这里也涵盖了最常见的用例和功能。
状态码
HTTP 状态码控制浏览器的行为,默认为 200 OK
。在大多数情况下,你无需手动设置 Response.status
属性,而是使用 abort()
助手或返回带有适当状态码的 HTTPResponse
实例。允许使用任何整数,但 HTTP 规范定义的代码之外的代码只会让浏览器感到困惑并违反标准。
响应头
响应头,例如 Cache-Control
或 Location
,通过 Response.set_header
定义。此方法接受两个参数,头名称和值。名称部分不区分大小写
@route('/wiki/<page>')
def wiki(page):
response.set_header('Content-Language', 'en')
...
大多数头是唯一的,意味着每个名称只有一个头发送给客户端。然而,一些特殊的头允许在响应中出现多次。要添加额外的头,请使用 Response.add_header
而不是 Response.set_header
response.set_header('Set-Cookie', 'name=value')
response.add_header('Set-Cookie', 'name=value2')
请注意,这只是一个示例。如果你想处理 cookie,请阅读后续内容。
重定向
要将客户端重定向到不同的 URL,你可以发送一个 303 See Other
响应,并将 Location
头设置为新的 URL。redirect()
会为你完成这项工作
from bottle import route, redirect
@route('/wrong/url')
def wrong():
redirect("/right/url")
请求数据¶
Cookie、HTTP 头、表单数据及其他请求数据均可通过全局 request
对象获取。即使在多线程环境中同时处理多个客户端连接,这个特殊对象也总是指向当前请求
from bottle import request, route, template
@route('/hello')
def hello():
name = request.cookies.username or 'Guest'
return template('Hello {{name}}', name=name)
request
对象是 BaseRequest
的子类,提供了丰富的 API 来访问数据。我们在这里只介绍最常用的功能,但这应该足以入门。
HTTP 头¶
客户端发送的所有 HTTP 头(例如 Referer
、Agent
或 Accept-Language
)都存储在 WSGIHeaderDict
中,可通过 Request.headers
属性访问。WSGIHeaderDict
本质上是一个键不区分大小写的字典
from bottle import route, request
@route('/is_ajax')
def is_ajax():
if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
return 'This is an AJAX request'
else:
return 'This is a normal request'
Cookies¶
Cookie 是存储在客户端浏览器中的一小段文本,每次请求都会发送回服务器。它们有助于在多个请求之间保持一些状态(HTTP 本身是无状态的),但不应用于安全相关的事务。它们很容易被客户端伪造。
客户端发送的所有 cookie 都可通过 Request.cookies
访问(一个 FormsDict
)。此示例展示了一个基于 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 'You visited this page %d times' % count
Request.get_cookie
方法是访问 cookie 的另一种方式。它支持解码在单独章节中描述的签名 cookie。
查询参数、表单和文件上传¶
查询和表单数据会按需解析,并通过 request
属性和方法访问。其中一些属性组合了来自不同来源的值,以便于访问。请看下表快速概览。
属性 |
数据源 |
---|---|
查询参数 |
|
|
|
表单字段和文件上传组合 |
|
表单字段 |
|
文件上传或非常大的表单字段 |
|
查询参数和表单字段组合 |
介绍 FormsDict
Bottle 使用一种特殊类型的字典来存储这些参数。FormsDict
的行为类似于普通字典,但有一些附加功能,使你的工作更轻松。
首先,FormsDict
是 MultiDict
的子类,并且可以为每个键存储多个值。默认情况下只返回第一个值,但可以使用 MultiDict.getall()
获取特定键的所有值的列表(可能为空)
for choice in request.forms.getall('multiple_choice'):
do_something(choice)
也支持类似属性的访问方式,对于缺失的值返回空字符串。这在处理大量可选属性时极大地简化了代码
name = request.query.name # may be an empty string
关于 Unicode 和字符编码的说明
请求路径、查询参数或 cookie 中的 Unicode 字符有点棘手。HTTP 是一个非常老的基于字节的协议,它早于 Unicode,并且缺乏明确的编码信息。这就是为什么 WSGI 服务器必须回退到 ISO-8859-1(也称为 latin1,一种可逆的输入编码)来处理这些字符串。不过,现代浏览器默认使用 utf8。要求应用开发者手动将每一个用户输入字符串转换为正确的编码,这有点过于繁重。Bottle 使这一切变得容易,并简单地假设一切都是 utf8 编码。Bottle API 返回的所有字符串都支持完整的 Unicode 字符范围,前提是网页或 HTTP 客户端遵循最佳实践并且不违反既定标准。
查询参数¶
查询字符串(如 /forum?id=1&page=5
)常用于向服务器传输少量键/值对。你可以使用 Request.query
属性(一个 FormsDict
)访问这些值,使用 Request.query_string
属性获取整个字符串。
from bottle import route, request, response, template
@route('/forum')
def display_forum():
forum_id = request.query.id
page = request.query.page or '1'
return template('Forum ID: {{id}} (page {{page}})', id=forum_id, page=page)
HTML <form> 处理¶
让我们从头开始。在 HTML 中,一个典型的 <form>
看起来像这样
<form action="/login" method="post">
Username: <input name="username" type="text" />
Password: <input name="password" type="password" />
<input value="Login" type="submit" />
</form>
action
属性指定接收表单数据的 URL。method
定义使用的 HTTP 方法(GET
或 POST
)。当 method="get"
时,表单值会被附加到 URL,并通过上面描述的 Request.query
访问。这有时被认为不安全且有其他限制,因此我们在这里使用 method="post"
。如果不确定,请使用 POST
表单。
通过 POST
传输的表单字段存储在 Request.forms
中,作为 FormsDict
。服务器端代码可能如下所示
from bottle import route, request
@route('/login')
def login():
return '''
<form action="/login" method="post">
Username: <input name="username" type="text" />
Password: <input name="password" type="password" />
<input value="Login" type="submit" />
</form>
'''
@route('/login', method='POST')
def do_login():
username = request.forms.username
password = request.forms.password
if check_login(username, password):
return "<p>Your login information was correct.</p>"
else:
return "<p>Login failed.</p>"
文件上传¶
为了支持文件上传,我们需要稍微修改 <form>
标签。首先,通过在 <form>
标签中添加 enctype="multipart/form-data"
属性来告知浏览器以不同的方式编码表单数据。然后,添加 <input type="file" />
标签以允许用户选择文件。这是一个示例
<form action="/upload" method="post" enctype="multipart/form-data">
Category: <input type="text" name="category" />
Select a file: <input type="file" name="upload" />
<input type="submit" value="Start upload" />
</form>
Bottle 将文件上传存储在 Request.files
中,作为 FileUpload
实例,并包含一些关于上传的元数据。假设你只是想将文件保存到磁盘
@route('/upload', method='POST')
def do_upload():
category = request.forms.category
upload = request.files.get('upload')
name, ext = os.path.splitext(upload.filename)
if ext not in ('.png','.jpg','.jpeg'):
return 'File extension not allowed.'
save_path = get_save_path_for_category(category)
upload.save(save_path) # appends upload.filename automatically
return 'OK'
FileUpload.filename
包含客户端文件系统上的文件名,但已清理和规范化,以防止文件名中包含不受支持的字符或路径段导致错误。如果你需要客户端发送的未经修改的文件名,请查看 FileUpload.raw_filename
。
如果你想将文件存储到磁盘,强烈推荐使用 FileUpload.save
方法。它能防止一些常见错误(例如,除非你明确告知,否则不会覆盖现有文件),并以内存高效的方式存储文件。你可以通过 FileUpload.file
直接访问文件对象,但请务必小心。
JSON¶
对于 JavaScript 和 REST API,向服务器发送 application/json
而不是表单数据非常常见。Request.json
属性包含解析后的数据结构(如果可用),对于空请求或不包含 application/json
数据的请求,则为 None
。解析错误会触发相应的 HTTPError
。
原始请求数据¶
你可以通过 Request.body
将原始主体数据作为类文件对象访问。根据内容长度和 Request.MEMFILE_MAX
设置,这可能是一个 io.BytesIO
缓冲区或一个临时文件。在这两种情况下,在你访问该属性之前,主体都已完全缓冲。如果你期望处理大量数据并希望直接无缓冲地访问流,请查看 request['wsgi.input']
。
WSGI 环境¶
每个 BaseRequest
实例都封装了一个 WSGI 环境字典,该字典存储在 Request.environ
中。大多数有趣的信息也通过特殊方法或属性暴露,但如果你想直接访问原始 WSGI environ,你可以这样做
@route('/my_ip')
def show_ip():
ip = request.environ.get('REMOTE_ADDR')
# or ip = request.get('REMOTE_ADDR')
# or ip = request['REMOTE_ADDR']
return template("Your IP is: {{ip}}", ip=ip)
模板¶
Bottle 内置了一个快速而强大的模板引擎,称为 SimpleTemplate。要渲染模板,你可以使用 template()
函数或 view()
装饰器。你只需提供模板名称以及要作为关键字参数传递给模板的变量即可。这是一个如何渲染模板的简单示例
@route('/hello')
@route('/hello/<name>')
def hello(name='World'):
return template('hello_template', name=name)
这将加载模板文件 hello_template.tpl
,并使用设置的 name
变量渲染它。Bottle 将在 ./views/
文件夹或 bottle.TEMPLATE_PATH
列表中指定的任何文件夹中查找模板。
view()
装饰器允许你返回一个包含模板变量的字典,而不是调用 template()
@route('/hello')
@route('/hello/<name>')
@view('hello_template')
def hello(name='World'):
return dict(name=name)
语法
模板语法是 Python 语言的一个非常薄的封装层。其主要目的是确保代码块的正确缩进,这样你就可以格式化你的模板而不用担心缩进问题。请点击链接查看完整的语法描述:SimpleTemplate 这里有一个示例模板
%if name == 'World':
<h1>Hello {{name}}!</h1>
<p>This is a test.</p>
%else:
<h1>Hello {{name.title()}}!</h1>
<p>How are you?</p>
%end
缓存
模板编译后会缓存在内存中。对模板文件所做的修改在清除模板缓存之前不会生效。调用 bottle.TEMPLATES.clear()
来执行此操作。在调试模式下,缓存是禁用的。
应用结构¶
Bottle 维护一个全局的 Bottle
实例栈,并将栈顶作为一些模块级函数和装饰器的默认应用。例如,route()
装饰器或 run() 函数是默认应用上 Bottle.route()
或 Bottle.run()
的快捷方式
@route('/')
def hello():
return 'Hello World'
if __name__ == '__main__':
run()
这对于小型应用非常方便,可以节省一些代码输入,但也意味着一旦你的模块被导入,路由就会安装到全局默认应用。为了避免这种导入副作用,Bottle 提供了第二种更显式的构建应用的方式
app = Bottle()
@app.route('/')
def hello():
return 'Hello World'
app.run()
分离应用对象也极大地提高了可重用性。其他开发者可以安全地从你的模块导入 app
对象,并使用 Bottle.mount()
将多个应用合并在一起。
0.13 版新增。
从 bottle-0.13 开始,你可以将 Bottle
实例用作上下文管理器
app = Bottle()
with app:
# Our application object is now the default
# for all shortcut functions and decorators
assert my_app is default_app()
@route('/')
def hello():
return 'Hello World'
# Also useful to capture routes defined in other modules
import some_package.more_routes
术语表¶
- callback (回调)¶
当发生某些外部操作时将被调用的程序员代码。在 Web 框架的上下文中,URL 路径与应用代码之间的映射通常通过为每个 URL 指定一个回调函数来实现。
- decorator (装饰器)¶
一个返回另一个函数的函数,通常使用
@decorator
语法作为函数转换应用。有关装饰器的更多信息,请参见Python 函数定义文档。- environ (环境)¶
一个结构,其中保存了根目录下所有文档的信息,并用于交叉引用。环境在解析阶段后被序列化 (pickled),以便后续运行只需读取和解析新的和更改的文档。
- handler function (处理函数)¶
用于处理特定事件或情况的函数。在 Web 框架中,通过将处理函数作为每个组成应用的特定 URL 的回调来开发应用。
- source directory (源目录)¶
包含一个 Sphinx 项目所有源文件的目录,包括其子目录。