初识Bottle(二)

初识Bottle(一)中,我们了解了Bottle的基本用法
Bottle源码阅读(一)Bottle源码阅读(二)可以查看个人对bottle源码的相关阅读笔记

下面继续阅读Bottle的官方文档https://bottlepy.org/docs/dev…

1.路由静态文件

在bottle中例如css等的文件是不会被自动加载的,因此需要自己定义callback函数,通过调用使用

from bottle import static_file
@route('/static/<filename>')
def server_static(filename):
    return static_file(filename, root='/path/to/your/static/files')

2.错误页

通过error装饰器可以对相应的错误码自定义对应的应用函数

from bottle import error
@error(404)
def error404(error):
    return 'Nothing here, sorry'

3.内容返回

Bottl框架允许应用函数返回如下类型的结果

Dictionaries字典:
python的字典通过框架被转换为json字符串,而header中的Content-Type则会被设置为application/json

Empty Strings, False, None or other non-true values:
header中的Content-Length被设置为0

Unicode strings
Byte strings
Instances of HTTPError or HTTPResponse
File objects
Iterables and generators

4.改变默认编码

通过在应用函数中改变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.'

5.静态文件的使用

通过static_file接口,我们可以实现调用和下载

from bottle import static_file
@route('/images/<filename:re:.*\.png>')
def send_image(filename):
    return static_file(filename, root='/path/to/image/files', mimetype='image/png')

@route('/static/<filename:path>')
def send_static(filename):
    return static_file(filename, root='/path/to/static/files')
    
@route('/download/<filename:path>')
def download(filename):
    return static_file(filename, root='/path/to/static/files', download=filename)

6.HTTP ERRORS AND REDIRECTS

通过abort可以快速定义错误码相应的错误页内容
redirect实现重定向

from bottle import route, abort
@route('/restricted')
def restricted():
    abort(401, "Sorry, access denied.")
    
from bottle import redirect
@route('/wrong/url')
def wrong():
    redirect("/right/url")

7.RESPONSE

response对象包括了响应的metadata例如状态码,headers,cookie等

response的状态码控制浏览器的行为,默认200ok
response的header可以通过set_header()来设置,对同一个header项,还可以通过add_header添加额外内容
cookie是保留在用户浏览器的一些数据,可以通过get_cookie和set_cookie来操作

@route('/hello')
def hello_again():
    if request.get_cookie("visited"):
        return "Welcome back! Nice to see you again"
    else:
        response.set_cookie("visited", "yes")
        return "Hello there! Nice to meet you"

cookie是容易被伪造的,所以Bottle框架提供了cookie的签名机制,只需要提供一个secret的参数作为密钥
Bottle会自动持久化和去持久化保存在cookie中的数据,cookie不超过4k。
此时cookie仍然能从浏览器中查看到,而且是可以被复制的,所以最好不要将密钥的信息放在用户客户端

@route('/login')
def do_login():
    username = request.forms.get('username')
    password = request.forms.get('password')
    if check_login(username, password):
        response.set_cookie("account", username, secret='some-secret-key')
        return template("<p>Welcome {{name}}! You are now logged in.</p>", name=username)
    else:
        return "<p>Login failed.</p>"

@route('/restricted')
def restricted_area():
    username = request.get_cookie("account", secret='some-secret-key')
    if username:
        return template("Hello {{name}}. Welcome back.", name=username)
    else:
        return "You are not logged in. Access denied."

8.REQUEST

request 对象包括了Cookies, HTTP header, HTML <form>等一系列可以操作的对象

from bottle import request, route, template

@route('/hello')
def hello():
    name = request.cookies.username or 'Guest'
    return template('Hello {{name}}', name=name)

Bottle使用FormsDict存储表单数据和cookie数据,而FormsDict的特定就是既具备了普通字典的操作方法,又能将key作为对象的属性,具体如下name既是字典的key又是cookies的属性

name = request.cookies.name

# is a shortcut for:

name = request.cookies.getunicode('name') # encoding='utf-8' (default)

# which basically does this:

try:
    name = request.cookies.get('name', '').decode('utf-8')
except UnicodeError:
    name = u''

同时,FormsDict还能对单个key存储多个值

for choice in request.forms.getall('multiple_choice'):
    do_something(choice)

request的query string会被分解为多个键值对,而通过request.query可以直接获得查询字符串对应键的值

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)

上传文件时,我们需要在表单添加enctype=”multipart/form-data”, 同时将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>

文件上传到服务端后

@route('/upload', method='POST')
def do_upload():
    category   = request.forms.get('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'

通过BaseRequest.body我们可以直接获取原始的请求体,也可以获取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)

9.模板

Bottle内建了前端模板引擎,在官网给出的这个例子中,会加载./views/中的hello_template.tpl

@route('/hello')
@route('/hello/<name>')
def hello(name='World'):
    return template('hello_template', name=name)
或者
@route('/hello')
@route('/hello/<name>')
@view('hello_template')
def hello(name='World'):
    return dict(name=name)
%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()清除缓存模板

10.PLUGINS

Bottle提供了一系列的第三方引擎,这些能够有效地减少重复性工作
官网使用SQLitePlugin作为例子,在每一次调用需要与数据库进行交互的callback函数时,该引擎会自动创建连接和关闭连接。而其他的引擎,例如’auth‘能够帮我们进行验证登录

from bottle import route, install, template
from bottle_sqlite import SQLitePlugin

install(SQLitePlugin(dbfile='/tmp/test.db'))

@route('/show/<post_id:int>')
def show(db, post_id):
    c = db.execute('SELECT title, content FROM posts WHERE id = ?', (post_id,))
    row = c.fetchone()
    return template('show_post', title=row['title'], text=row['content'])

@route('/contact')
def contact_page():
    ''' This callback does not need a db connection. Because the 'db'
        keyword argument is missing, the sqlite plugin ignores this callback
        completely. '''
    return template('contact')
sqlite_plugin = SQLitePlugin(dbfile='/tmp/test.db')
install(sqlite_plugin)

uninstall(sqlite_plugin) # uninstall a specific plugin
uninstall(SQLitePlugin)  # uninstall all plugins of that type
uninstall('sqlite')      # uninstall all plugins with that name
uninstall(True)          # uninstall all plugins at once

自此,Bottle官网的tutorial我们已经大致有了了解
后续我们可以选择运用该框架实现一些简单的应用,或者可以深入研究其源码,提升自身的编程水平

    原文作者:whales
    原文地址: https://segmentfault.com/a/1190000008417949
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞