您的当前位置:首页正文

想用Python做后端api?Flask简单入个门!

2024-11-27 来源:个人技术集锦

java有一套spring但是略显笨重而且对于个人平时玩玩来说总感觉没必要(如果掌握了的话效率还是不错的) python的web框架有django和flask等

先安装环境

pip install flask

即可 安装最新版

按照官网教程

最小的应用

from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'Hello, World!'

WSGI的介绍

全称Python Web Server Gateway Interface,指定了web服务器和Python web应用或web框架之间的标准接口,以提高web应用在一系列web服务器间的移植性。 具体可查看

从以上介绍我们可以看出:

  1. WSGI是一套接口标准协议/规范;
  2. 通信(作用)区间是Web服务器和Python Web应用程序之间;
  3. 目的是制定标准,以保证不同Web服务器可以和不同的Python程序之间相互通信

请不要使用 flask.py 作为应用名称,这会与 Flask 本身发生冲突。

flask 命令或者 python 的 -m 开关来运行这个应用。在 运行应用之前,需要在终端里导出 FLASK_APP 环境变量:

$ export FLASK_APP=hello.py
$ flask run
 * Running on http://127.0.0.1:5000/

通过flask run -h [host] -p [port]指定主机和端口

export FLASK_ENV=development

打开开发环境的功能

  1. 激活调试器。
  2. 激活自动重载。
  3. 打开 Flask 应用的调试模式。

还可以通过导出 FLASK_DEBUG=1 来单独控制调试模式的开关,绝对不能在生产环境 中使用调试器

路由

现代 web 应用都使用有意义的 URL ,这样有助于用户记忆,网页会更得到用户的青睐, 提高回头率

from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
    return 'index page'
# print(__name__)
@app.route('/hello')
def hello():
    return 'hello world'

可以动态变化 URL 的某些部分, 还可以为一个函数指定多个规则

@app.route('/user/<username>')
def show_user_profile(username):
    # 显示用户名
    return 'User {}'.format(username)

@app.route('/post/<int:post_id>')
def show_post(post_id):
    # 显示提交整型的用户"id"的结果,注意"int"是将输入的字符串形式转换为整型数据
    return 'Post {}'.format(post_id)

@app.route('/path/<path:subpath>')
def show_subpath(subpath):
    # 显示 /path/ 之后的路径名
    return 'Subpath {}'.format(subpath)

转换器的主要类型如下:

类型含义
string默认的数据类型,接受没有任何斜杠“/”的字符串
int接受整型
float接受浮点类型
path和 string 类似,但是接受斜杠“/”
uuid只接受 uuid 字符串

唯一的 URL / 重定向行为

以下两条规则的不同之处在于是否使用尾部的斜杠。:

@app.route('/projects/')
def projects():
    return 'The project page'

@app.route('/about')
def about():
    return 'The about page'

projects 的 URL 是中规中矩的,尾部有一个斜杠,看起来就如同一个文件夹。 访问一个没有斜杠结尾的 URL 时 Flask 会自动进行重定向,帮你在尾部加上一个斜杠。

about 的 URL 没有尾部斜杠,因此其行为表现与一个文件类似。如果访问这个 URL 时添加了尾部斜杠就会得到一个 404 错误。这样可以保持 URL 唯一,并帮助 搜索引擎避免重复索引同一页面。

构建URL

使用url_for()函数

函数用于构建指定函数的 URL。它把函数名称作为第一个 参数。它可以接受任意个关键字参数,每个关键字参数对应 URL 中的变量。未知变量 将添加到 URL 中作为查询参数。

为什么不在把 URL 写死在模板中,而要使用反转函数 动态构建?

  1. 反转通常比硬编码 URL 的描述性更好。
  2. 你可以只在一个地方改变 URL ,而不用到处乱找。
  3. URL 创建会为你处理特殊字符的转义和 Unicode 数据,比较直观。
  4. 生产的路径总是绝对路径,可以避免相对路径产生副作用。
  5. 如果你的应用是放在 URL 根路径之外的地方(如在 /myapplication 中,不在 / 中), 会为你妥善处理。

HTTP方法

Web 应用使用不同的 HTTP 方法处理 URL 。当你使用 Flask 时,应当熟悉 HTTP 方法。 缺省情况下,一个路由只回应 GET 请求。 可以使用 装饰器的 methods 参数来处理不同的 HTTP 方法:

from flask import request

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        return do_the_login()
    else:
        return show_the_login_form()

如果当前使用了 GET 方法, Flask 会自动添加 HEAD 方法支持,并且同时还会 按照 来处理 HEAD 请求。同样, OPTIONS 也会自动实现。

静态文件

动态的 web 应用也需要静态文件,一般是 CSS 和 JavaScript 文件。理想情况下你的 服务器已经配置好了为你的提供静态文件的服务。但是在开发过程中, Flask 也能做好 这项工作。只要在你的包或模块旁边创建一个名为 static 的文件夹就行了。 静态文件位于应用的 /static 中。

使用特定的 'static' 端点就可以生成相应的 URL

url_for('static', filename='style.css')

这个静态文件在文件系统中的位置应该是 static/style.css

渲染模板

在 Python 内部生成 HTML 不好玩,且相当笨拙。因为你必须自己负责 HTML 转义, 以确保应用的安全。因此, Flask 自动为你配置 模板引擎

Flask 将会在 templates 文件夹中寻找模板。因此如果你的应用是个模块,这个文件夹在模块的旁边,如果它是一个包,那么这个文件夹在你的包里面

<!DOCTYPE html>

<title>Hello From Flask</title>

{% if name %}
<!-- 如果 name 不为空则将 name 渲染出来 -->
<h1>Hello {{ name }}!</h1>
{% else %}
<!-- 如果 name 为空则打印 Hello World! -->
<h1>Hello World!</h1>
{% endif %}

在模板中你也可以使用 、 和 对象,也能使用函数 。

模板继承是十分有用的。如果想要知道模板继承如何工作的话,请阅读文档。基本的模板继承使得某些特定元素(如标题、导航和页脚)在每一页成为可能

引入css文件

<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <link
      rel="stylesheet"
      type="text/css"
      href="{{ url_for('static', filename='style.css') }}"
    />
  </head>
  <body>
    <h1>Hello ShiYanLou {{name}}</h1>
  </body>
</html>

接收请求数据

在 Flask 中由全局对象 request 来提供这些信息。如果你有一定的 Python 经验,你会好奇这个对象怎么可能是全局的,并且 Flask 是怎么还能保证线程安全。答案是上下文作用域

请求对象

首先你需要从 flask 模块中导入 request

from flask import request

当前请求的方法可以用method属性来访问。你可以用form属性来访问表单数据 (数据在 POST 或者PUT中传输)。

如果在form属性中不存在上述键值会发生些什么?在这种情况下会触发一个特别的 KeyError。你可以像捕获标准的KeyError一样来捕获它,如果你不这样去做,会显示一个HTTP 400 Bad Request错误页面。所以很多情况下你不需要处理这个问题。

你可以用args属性来接收在URL ( ?key=value )中提交的参数:

searchword = request.args.get('key', '')

我们推荐使用get来访问 URL 参数或捕获KeyError,因为用户可能会修改 URL,向他们显示一个400 bad request页面不是用户友好的。

文件上传

你能够很容易地用 Flask 处理文件上传。只要确保在你的 HTML 表单中不要忘记设置属性 enctype="multipart/form-data" ,否则浏览器将不会传送文件。

上传的文件是存储在内存或者文件系统上一个临时位置。你可以通过请求对象中files属性访问这些文件。每个上传的文件都会存储在这个属性字典里。它表现得像一个标准的 Python file对象,但是它同样具有save()方法,该方法允许你存储文件在服务器的文件系统上。

已上传的文件被储存在内存或文件系统的临时位置。你可以通过请求对象 files 属性来访问上传的文件。每个上传的文件都储存在这个 字典型属性中。这个属性基本和标准 Python file 对象一样,另外多出一个 用于把上传文件保存到服务器的文件系统中的 方法。下例展示其如何运作:

from flask import request

@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
    if request.method == 'POST':
        f = request.files['the_file']
        f.save('/var/www/uploads/uploaded_file.txt')

如果你想要知道在上传到你的应用之前在客户端的文件名称,你可以访问filename属性。但请记住永远不要信任这个值,因为这个值可以伪造。如果你想要使用客户端的文件名来在服务器上存储文件,把它传递到 Werkzeug 提供给你的 secure_filename() 函数:

from flask import request
from werkzeug import secure_filename

@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
    if request.method == 'POST':
        f = request.files['the_file']
        f.save('/var/www/uploads/' + secure_filename(f.filename))
    ...

文件上传方案

import os
from flask import Flask, flash, request, redirect, url_for
from werkzeug.utils import secure_filename

UPLOAD_FOLDER = '/path/to/the/uploads'
ALLOWED_EXTENSIONS = {'txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif'}

app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER

下一个函数检查扩展名是否合法,上传文件,把用户重定向到已上传文件的 URL:

def allowed_file(filename):
    return '.' in filename and \
           filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS

@app.route('/', methods=['GET', 'POST'])
def upload_file():
    if request.method == 'POST':
        # check if the post request has the file part
        if 'file' not in request.files:
            flash('No file part')
            return redirect(request.url)
        file = request.files['file']
        # if user does not select file, browser also
        # submit an empty part without filename
        if file.filename == '':
            flash('No selected file')
            return redirect(request.url)
        if file and allowed_file(file.filename):
            filename = secure_filename(file.filename)
            file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
            return redirect(url_for('uploaded_file',
                                    filename=filename))
    return '''
    <!doctype html>
    <title>Upload new File</title>
    <h1>Upload new File</h1>
    <form method=post enctype=multipart/form-data>
      <input type=file name=file>
      <input type=submit value=Upload>
    </form>
    '''

那么 函数到底是有什么用?有一条原 则是“永远不要信任用户输入”。这条原则同样适用于已上传文件的文件名。所有提 交的表单数据可能是伪造的,文件名也可以是危险的。此时要谨记:在把文件保存到 文件系统之前总是要使用这个函数对文件名进行安检。

cookies

要访问 cookies ,可以使用 属性。可以使用响应 对象 的 方法来设置 cookies 。请求对象的 属性是一个包含了客户端传输的所有 cookies 的字典。在 Flask 中,如果使用 ,那么就不要直接使用 cookies ,因为 比较安全一些。

读取 cookies:

from flask import request

@app.route('/')
def index():
    username = request.cookies.get('username')
    # use cookies.get(key) instead of cookies[key] to not get a
    # KeyError if the cookie is missing.

重定向

使用 函数可以重定向。使用 可以 更早退出请求,并返回错误代码:

from flask import abort, redirect, url_for

@app.route('/')
def index():
    return redirect(url_for('login'))

@app.route('/login')
def login():
    abort(401)
    this_is_never_executed()

上例实际上是没有意义的,它让一个用户从索引页重定向到一个无法访问的页面(401 表示禁止访问)。但是上例可以说明重定向和出错跳出是如何工作的。

缺省情况下每种出错代码都会对应显示一个黑白的出错页面。使用 装饰器可以定制出错页面:

from flask import render_template

@app.errorhandler(404)
def page_not_found(error):
    return render_template('page_not_found.html'), 404

注意 后面的 404 ,这表示页面对就的出错 代码是 404 ,即页面不存在。缺省情况下 200 表示:一切正常

响应

一个视图函数的返回值会被自动转换为一个响应对象。如果返回值是一个字符串,它被转换成一个响应主体是该字符串,错误代码为 200 OK ,媒体类型为text/html的响应对象。Flask 把返回值转换成响应对象的逻辑如下:

  1. 如果返回的是一个合法的响应对象,它会直接从视图返回。
  2. 如果返回的是一个字符串,响应对象会用字符串数据和默认参数创建。
  3. 如果返回的是一个元组而且元组中元素能够提供额外的信息。这样的元组必须是(response, status, headers) 形式且至少含有其中的一个元素。status值将会覆盖状态代码,headers可以是一个列表或额外的消息头值字典。
  4. 如果上述条件均不满足,Flask 会假设返回值是一个合法的 WSGI 应用程序,并转换为一个请求对象

会话

除了请求对象之外还有一种称为 的对象,允许你在不同请求 之间储存信息。这个对象相当于用密钥签名加密的 cookie ,即用户可以查看你的 cookie ,但是如果没有密钥就无法修改它。

使用会话之前你必须设置一个密钥。举例说明:

from flask import Flask, session, redirect, url_for, escape, request

app = Flask(__name__)

# Set the secret key to some random bytes. Keep this really secret!
app.secret_key = b'_5#y2L"F4Q8z\n\xec]/'

@app.route('/')
def index():
    if 'username' in session:
        return 'Logged in as %s' % escape(session['username'])
    return 'You are not logged in'

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        session['username'] = request.form['username']
        return redirect(url_for('index'))
    return '''
        <form method="post">
            <p><input type=text name=username>
            <p><input type=submit value=Login>
        </form>
    '''

@app.route('/logout')
def logout():
    # remove the username from the session if it's there
    session.pop('username', None)
    return redirect(url_for('index'))

这里用到的 是用来转义的。如果不使用模板引擎就可以像上例 一样使用这个函数来转义

消息闪烁

好的应用和用户界面全部是关于反馈。如果用户得不到足够的反馈,他们可能会变得讨厌这个应用。Flask 提供了一个真正的简单的方式来通过消息闪现系统给用户反馈。消息闪现系统基本上使得在请求结束时记录信息并在下一个 (且仅在下一个)请求中访问。通常结合模板布局来显示消息。

使用flash()方法来闪现一个消息,使用get_flashed_messages()能够获取消息,get_flashed_messages()也能用于模板中。

日志和整合 WSGI 中间件

日志

有时候你会遇到一种情况:理论上来说你处理的数据应该是正确的,然而实际上并不正确。比如你可能有一些客户端代码,代码向服务器发送一个 HTTP 请求,但是显然它是错误的。这可能是由于用户篡改数据,或客户端代码失败。大部分时候针对这一情况返回400 Bad Request就可以了,但是有时候不能这样做,代码必须继续工作。

你也有可能想要记录一些发生的不正常事情。这时候日志就派上用处。从 Flask 0.3 开始日志记录是预先配置好的

整合 WSGI 中间件

如果你想给你的应用添加 WSGI 中间件,你可以封装内部 WSGI 应用。例如如果你想使用 Werkzeug 包中的某个中间件来应付 lighttpd 中的 bugs,你可以这样做:

from werkzeug.contrib.fixers import LighttpdCGIRootFix
app.wsgi_app = LighttpdCGIRootFix(app.wsgi_app)

实践

包括四个视图函数的编写:主页视图、新建博客视图、登录视图和等初始图以及三个模板文件。最后编写了针对页面进行美化的 style.css 文件。完成后,即可启动项目并执行登录、编写博客等操作

参考资料

显示全文