# Bottle 教程

> 原文： [http://zetcode.com/python/bottle/](http://zetcode.com/python/bottle/)

Bottle 教程展示了如何使用 Python Bottle Web 微框架在 Python 中创建简单的 Web 应用。

## Bottle 

Bottle 是用于 Python 的快速，简单且轻量级的 WSGI 微型网络框架。 它作为单个文件模块分发。 除 Python 标准库外，没有其他依赖项。

Web 服务器网关接口（WSGI）是 Web 服务器的简单调用约定，用于将请求转发到以 Python 编程语言编写的 Web 应用或框架。

## Bottle 安装

```py
$ sudo pip3 install bottle

```

我们使用`pip3`工具安装 Bottle。

## Bottle 简单的例子

在下面的示例中，我们创建一个简单的 Bottle 应用。

```py
$ mkdir simple && cd simple
$ touch simple.py

```

我们创建一个项目目录一个 Python 文件。

`simple.py`

```py
#!/usr/bin/env python3

from bottle import route, run

@route('/message')
def hello():
    return "Today is a beautiful day"  

run(host='localhost', port=8080, debug=True)

```

该示例向客户端发送一条消息。

```py
from bottle import route, run

```

我们导入`route`装饰器和`run`函数。 `route`装饰器用于将功能绑定到请求 URL。 `run`功能启动服务器实例。 默认情况下，它是开发服务器。

```py
@route('/message')
def hello():
    return "Today is a beautiful day"  

```

使用`@route()`装饰器，我们定义了一条路线。 路由是 URL 与 Web 服务器功能之间的映射。 在我们的例子中，该功能返回一条简单的文本消息。

```py
run(host='localhost', port=8080, debug=True)

```

我们以调试模式在端口 8080 上启动服务器。

```py
$ ./simple.py 
Bottle v0.12.13 server starting up (using WSGIRefServer())...
Listening on http://localhost:8080/
Hit Ctrl-C to quit.

```

我们启动开发服务器。

```py
$ curl localhost:8080/message
Today is a beautiful day

```

我们使用`curl`工具创建一个请求。 服务器以一条简单消息响应。

## Bottle  JSON 响应

Web 应用通常以 JSON 格式发送响应。 Bottle 自动将 Python 字典转换为 JSON。

`json_response.py`

```py
#!/usr/bin/env python3

from bottle import route, run

@route('/cars')
def getcars():

    cars = [ {'name': 'Audi', 'price': 52642},
        {'name': 'Mercedes', 'price': 57127},
        {'name': 'Skoda', 'price': 9000},
        {'name': 'Volvo', 'price': 29000},
        {'name': 'Bentley', 'price': 350000},
        {'name': 'Citroen', 'price': 21000},
        {'name': 'Hummer', 'price': 41400},
        {'name': 'Volkswagen', 'price': 21600} ]

    return dict(data=cars)

run(host='localhost', port=8080, debug=True)

```

该应用将有关汽车的数据作为 JSON 发送到客户端。

```py
return dict(data=cars)

```

Bottle 将 Python 字典转换为 JSON。

```py
$ curl localhost:8080/cars
{"data": [{"name": "Audi", "price": 52642}, {"name": "Mercedes", "price": 57127}, 
{"name": "Skoda", "price": 9000}, {"name": "Volvo", "price": 29000}, 
{"name": "Bentley", "price": 350000}, {"name": "Citroen", "price": 21000}, 
{"name": "Hummer", "price": 41400}, {"name": "Volkswagen", "price": 21600}]}

```

我们收到一个命名的 JSON 数组。

## Bottle 获取请求

HTTP GET 方法请求指定资源的表示形式。 在 Bottle 中，我们可以使用`@route`或`@get`装饰器映射 GET 请求。 从`request.query`检索数据。

GET 请求通常是默认的请求方法。

`get_request.py`

```py
#!/usr/bin/env python3

from bottle import route, run, request, get

@get('/msg')
def message():

    name = request.query.name
    age = request.query.age

    return "{0} is {1} years old".format(name, age)

run(host='localhost', port=8080, debug=True)

```

该应用根据 GET 请求的数据构建一条消息。

```py
@get('/msg')
def message():

```

`message()`函数通过`/msg`路径映射到 GET 请求。 `@get('msg')`装饰器等效于`@route('msg', method='GET')`，或更短的`@route('msg')`。

```py
name = request.query.name
age = request.query.age

```

我们从查询字符串中检索数据。

```py
$ curl "localhost:8080/greet?name=Peter&age=34"
Peter is 34 years old

```

我们使用`curl`工具发出 GET 请求。 GET 请求是`curl`的默认请求。 我们将`name`和`age`参数添加到查询字符串。

## Bottle 静态文件

使用`static_file()`，我们可以在 Bottle 中提供静态文件。

```py
$ mkdir botstat && cd botstat
$ mkdir public 
$ touch public/home.html app.py

```

我们为应用创建目录和文件。

`public/home.html`

```py
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Home page</title>
</head>
<body>
    <p>This is home page</p>
</body>
</html>

```

这是位于`public`目录中的主页。 静态资源的目录通常称为`public`或`static`。

`app.py`

```py
#!/usr/bin/env python3

from bottle import route, run, static_file

@route('/<filepath:path>')
def server_static(filepath):
    return static_file(filepath, root='./public/')

run(host='localhost', port=8080, debug=True)

```

在此示例中，我们提供静态文件。 为了获得主页，我们必须导航到`localhost:8080/home.html`。

```py
@route('/<filepath:path>')

```

`filepath:path`是仅允许出现在包含斜杠的路径中的字符的过滤器。

```py
return static_file(filepath, root='./public/')

```

通过`static_file()`功能，我们可以提供静态文件。 静态文件所在的目录在`root`参数中指定。

## Bottle 过滤器

包含通配符的路由称为动态路由（与静态路由相对）。 它们可以同时匹配多个 URL。 通配符由括在尖括号中的名称组成（例如`<name>`），并且可以接受一个或多个字符，直到下一个斜杠为止。

过滤器可用于定义更特定的通配符。

*   `:int`匹配（带符号）数字
*   `:float`匹配十进制数字
*   `:path`路径段中允许使用的数学字符
*   `:re`允许指定自定义正则表达式

`filters.py`

```py
#!/usr/bin/env python3

from bottle import route, run

@route('/app/<myid:int>/')
def provide(myid):
    return "Object with id {} returned".format(myid)

@route('/app/<name:re:[a-z]+>/')
def provide(name):
    return "Name {} given".format(name)    

run(host='localhost', port=8080, debug=True)

```

该示例使用整数过滤器和正则表达式过滤器。

```py
$ curl localhost:8080/app/3/
Object with id 3 returned

```

在这里，我们向路径添加一个整数。

## Bottle 例子

在下面的示例中，我们将表单发送到 Bottle 应用。

```py
$ mkdir simple_form && cd simple_form
$ mkdir public 
$ touch public/index.html simple_form.py

```

我们为应用创建目录和文件。

`public/index.html`

```py
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Home page</title>
</head>

<body>

    <form method="post" action="doform">
        <div>
            <label for="name">Name:</label>
            <input type="text" id="name" name="name">
        </div>
        <div>
            <label for="occupation">Occupation:</label>
            <input type="text" id="occupation" name="occupation">
        </div>
        <button type="submit">Submit</button>
    </form>

</body>

</html>

```

在 HTML 文件中，我们有一个表单标签。 该表格包含两个输入字段：名称和职业。

`simple_form.py`

```py
#!/usr/bin/env python3

from bottle import route, run, post, request, static_file

@route('/')
def server_static(filepath="index.html"):
    return static_file(filepath, root='./public/')

@post('/doform')
def process():

    name = request.forms.get('name')
    occupation = request.forms.get('occupation')
    return "Your name is {0} and you are a(n) {1}".format(name, occupation)

run(host='localhost', reloader=True, port=8080, debug=True)

```

在`simple_form.py`文件中，我们提供一个表格并处理该表格。

```py
@route('/')
def server_static(filepath="index.html"):
    return static_file(filepath, root='./public/')

```

对于根路径（/），我们从`public`目录提供`index.html`。

```py
@post('/doform')
def process():

    name = request.forms.get('name')
    occupation = request.forms.get('occupation')
    return "Your name is {0} and you are a(n) {1}".format(name, occupation)

```

在这里，我们处理表格。 我们使用`@post`装饰器。 我们从`request.forms`获取数据并构建消息字符串。

## Bottle 错误处理程序

可以使用`@error`装饰器创建自定义错误页面。

`error_handler.py`

```py
#!/usr/bin/env python3

from bottle import route, run, error

@route('/app/<myid:int>')
def provide(myid):
    return "Object with id {} returned".format(myid)

@error(404)
def error404(error):
    return '404 - the requested page could not be found'   

run(host='localhost', port=8080, debug=True)

```

在此示例中，我们在自定义错误处理程序中处理 404 错误。

```py
@error(404)
def error404(error):
    return '404 - the requested page could not be found'   

```

`@error`装饰器将错误代码作为参数。

```py
$ curl localhost:8080/app/Peter
404 - the requested page could not be found

```

我们尝试访问未定义的路由； 我们会收到自定义错误消息。

## Bottle MongoDB 示例

在以下示例中，我们从 MongoDB 数据库以 JSON 形式返回数据。

`create_cars.py`

```py
#!/usr/bin/python3

from pymongo import MongoClient

cars = [ {'name': 'Audi', 'price': 52642},
    {'name': 'Mercedes', 'price': 57127},
    {'name': 'Skoda', 'price': 9000},
    {'name': 'Volvo', 'price': 29000},
    {'name': 'Bentley', 'price': 350000},
    {'name': 'Citroen', 'price': 21000},
    {'name': 'Hummer', 'price': 41400},
    {'name': 'Volkswagen', 'price': 21600} ]

client = MongoClient('mongodb://localhost:27017/')

with client:

    db = client.testdb

    db.cars.insert_many(cars)

```

使用此脚本，我们创建一个 Mongo 集合。 有关在 Python 中使用 MongoDB 的更多信息，请参考 [PyMongo 教程](/python/pymongo/)。

`bottle_mongo.py`

```py
#!/usr/bin/env python3

from bottle import route, run, HTTPResponse
from pymongo import MongoClient
import json

client = MongoClient('mongodb://localhost:27017/')

@route('/cars')
def getcars():

    db = client.testdb
    cars = list(db.cars.find({}, {'_id': 0}))

    if cars:

        return json.dumps(cars)
    else: 
        raise HTTPResponse(status=204)

run(host='localhost', port=8080, debug=True)

```

该示例从 Mongo 集合返回数据作为 JSON。

```py
client = MongoClient('mongodb://localhost:27017/')

```

创建一个`MongoClient`实例。

```py
db = client.testdb
cars = list(db.cars.find({}, {'_id': 0}))

```

我们从两个字段中检索所有数据； 我们排除`_id`字段。

```py
if cars:

    return json.dumps(cars)
else: 
    raise HTTPResponse(status=204)

```

如果有数据，我们将使用`json.dumps()`将其转换为 JSON，然后将其返回给客户端。 否则，我们会发送 204 状态代码。

## Bottle 模板示例

模板引擎是一个旨在将模板与数据模型结合以生成结果文档的库。 默认情况下，Bottle 使用简单的模板引擎。

```py
$ mkdir botview && cd botview
$ mkdir views 
$ touch views/show_cars.tpl app.py

```

我们为应用创建目录和文件。

`views/show_cars.tpl`

```py
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Cars</title>
</head>
<body>

    <table>

        <tr>
            <th>Name</th>
            <th>Price</th>
        </tr>
        % for car in cars:
        <tr>
            <td>{{car['name']}}</td>
            <td>{{car['price']}}</td>
        </tr>
        % end

    </table>

</body>
</html>

```

在此模板中，我们浏览接收到的`cars`对象并从中生成一个表。 模板文件位于`views`目录中。

`app.py`

```py
#!/usr/bin/env python3

from bottle import route, run, template, HTTPResponse
from pymongo import MongoClient

client = MongoClient('mongodb://localhost:27017/')

@route('/cars')
def getcars():

    db = client.testdb
    data = db.cars.find({}, {'_id': 0})

    if data:

        return template('show_cars', cars=data)
    else: 
        return HTTPResponse(status=204)

run(host='localhost', port=8080, debug=True)

```

在应用中，我们从 MongoDB 集合中检索数据。 我们使用`template()`功能将模板文件与数据结合在一起。

在本教程中，我们使用 Bottle 在 Python 中创建简单的 Web 应用。

您可能也对以下相关教程感兴趣： [PyMongo 教程](/python/pymongo/)， [Jinja 教程](/python/jinja/)和 [Python 教程](/lang/python/)。